blob: d239dfbc7af83c091cb083e18b662b43ebc2558f [file] [log] [blame]
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
package org.apache.james.mime4j.storage;
import java.io.IOException;
import java.io.InputStream;
/**
* <p>
* A wrapper around another {@link Storage} that also maintains a reference
* counter. The inner storage gets deleted only if the reference counter reaches
* zero.
* </p>
* <p>
* Reference counting is used to delete the storage when it is no longer needed.
* So, any users of this class should note:
* </p>
* <ul>
* <li>The reference count is set up one on construction. In all other cases,
* {@link #addReference()} should be called when the storage is shared.</li>
* <li>The caller of {@link #addReference()} should ensure that
* {@link #delete()} is called once and only once.</li>
* <li>Sharing the {@link Storage} instance passed into
* {@link #MultiReferenceStorage(Storage)} may lead to miscounting and premature
* deletion</li>
* </ul>
*/
public class MultiReferenceStorage implements Storage {
private final Storage storage;
private int referenceCounter;
/**
* Creates a new <code>MultiReferenceStorage</code> instance for the given
* back-end. The reference counter is initially set to one so the caller
* does not have to call {@link #addReference()} after this constructor.
*
* @param storage
* storage back-end that should be reference counted.
* @throws IllegalArgumentException
* when storage is null
*/
public MultiReferenceStorage(Storage storage) {
if (storage == null)
throw new IllegalArgumentException();
this.storage = storage;
this.referenceCounter = 1; // caller holds first reference
}
/**
* Increments the reference counter.
*
* @throws IllegalStateException
* if the reference counter is zero which implies that the
* backing storage has already been deleted.
*/
public void addReference() {
incrementCounter();
}
/**
* Decrements the reference counter and deletes the inner
* <code>Storage</code> object if the reference counter reaches zero.
* <p>
* A client that holds a reference to this object must make sure not to
* invoke this method a second time.
*
* @throws IllegalStateException
* if the reference counter is zero which implies that the
* backing storage has already been deleted.
*/
public void delete() {
if (decrementCounter()) {
storage.delete();
}
}
/**
* Returns the input stream of the inner <code>Storage</code> object.
*
* @return an input stream.
*/
public InputStream getInputStream() throws IOException {
return storage.getInputStream();
}
/**
* Synchronized increment of reference count.
*
* @throws IllegalStateException
* when counter is already zero
*/
private synchronized void incrementCounter() {
if (referenceCounter == 0)
throw new IllegalStateException("storage has been deleted");
referenceCounter++;
}
/**
* Synchronized decrement of reference count.
*
* @return true when counter has reached zero, false otherwise
* @throws IllegalStateException
* when counter is already zero
*/
private synchronized boolean decrementCounter() {
if (referenceCounter == 0)
throw new IllegalStateException("storage has been deleted");
return --referenceCounter == 0;
}
}