001 /**************************************************************** 002 * Licensed to the Apache Software Foundation (ASF) under one * 003 * or more contributor license agreements. See the NOTICE file * 004 * distributed with this work for additional information * 005 * regarding copyright ownership. The ASF licenses this file * 006 * to you under the Apache License, Version 2.0 (the * 007 * "License"); you may not use this file except in compliance * 008 * with the License. You may obtain a copy of the License at * 009 * * 010 * http://www.apache.org/licenses/LICENSE-2.0 * 011 * * 012 * Unless required by applicable law or agreed to in writing, * 013 * software distributed under the License is distributed on an * 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 015 * KIND, either express or implied. See the License for the * 016 * specific language governing permissions and limitations * 017 * under the License. * 018 ****************************************************************/ 019 020 package org.apache.james.mime4j.storage; 021 022 import java.io.IOException; 023 import java.io.InputStream; 024 025 /** 026 * <p> 027 * A wrapper around another {@link Storage} that also maintains a reference 028 * counter. The inner storage gets deleted only if the reference counter reaches 029 * zero. 030 * </p> 031 * <p> 032 * Reference counting is used to delete the storage when it is no longer needed. 033 * So, any users of this class should note: 034 * </p> 035 * <ul> 036 * <li>The reference count is set up one on construction. In all other cases, 037 * {@link #addReference()} should be called when the storage is shared.</li> 038 * <li>The caller of {@link #addReference()} should ensure that 039 * {@link #delete()} is called once and only once.</li> 040 * <li>Sharing the {@link Storage} instance passed into 041 * {@link #MultiReferenceStorage(Storage)} may lead to miscounting and premature 042 * deletion</li> 043 * </ul> 044 */ 045 public class MultiReferenceStorage implements Storage { 046 047 private final Storage storage; 048 private int referenceCounter; 049 050 /** 051 * Creates a new <code>MultiReferenceStorage</code> instance for the given 052 * back-end. The reference counter is initially set to one so the caller 053 * does not have to call {@link #addReference()} after this constructor. 054 * 055 * @param storage 056 * storage back-end that should be reference counted. 057 * @throws IllegalArgumentException 058 * when storage is null 059 */ 060 public MultiReferenceStorage(Storage storage) { 061 if (storage == null) 062 throw new IllegalArgumentException(); 063 064 this.storage = storage; 065 this.referenceCounter = 1; // caller holds first reference 066 } 067 068 /** 069 * Increments the reference counter. 070 * 071 * @throws IllegalStateException 072 * if the reference counter is zero which implies that the 073 * backing storage has already been deleted. 074 */ 075 public void addReference() { 076 incrementCounter(); 077 } 078 079 /** 080 * Decrements the reference counter and deletes the inner 081 * <code>Storage</code> object if the reference counter reaches zero. 082 * <p> 083 * A client that holds a reference to this object must make sure not to 084 * invoke this method a second time. 085 * 086 * @throws IllegalStateException 087 * if the reference counter is zero which implies that the 088 * backing storage has already been deleted. 089 */ 090 public void delete() { 091 if (decrementCounter()) { 092 storage.delete(); 093 } 094 } 095 096 /** 097 * Returns the input stream of the inner <code>Storage</code> object. 098 * 099 * @return an input stream. 100 */ 101 public InputStream getInputStream() throws IOException { 102 return storage.getInputStream(); 103 } 104 105 /** 106 * Synchronized increment of reference count. 107 * 108 * @throws IllegalStateException 109 * when counter is already zero 110 */ 111 private synchronized void incrementCounter() { 112 if (referenceCounter == 0) 113 throw new IllegalStateException("storage has been deleted"); 114 115 referenceCounter++; 116 } 117 118 /** 119 * Synchronized decrement of reference count. 120 * 121 * @return true when counter has reached zero, false otherwise 122 * @throws IllegalStateException 123 * when counter is already zero 124 */ 125 private synchronized boolean decrementCounter() { 126 if (referenceCounter == 0) 127 throw new IllegalStateException("storage has been deleted"); 128 129 return --referenceCounter == 0; 130 } 131 }