001 package org.apache.ojb.broker.cache;
002
003 /* Copyright 2004-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import java.util.HashMap;
019 import java.util.Iterator;
020 import java.util.Map;
021
022 import org.apache.ojb.broker.Identity;
023 import org.apache.ojb.broker.util.logging.Logger;
024 import org.apache.ojb.broker.util.logging.LoggerFactory;
025
026 /**
027 * A wrapper class for {@link ObjectCache} implementations used to materialize object graphs and
028 * push the fully materialized object to the real object cache.
029 * To avoid passing of partial materialized objects to cache this class act as a temporary storage
030 * for unmaterialized (new read or refreshed) objects.
031 *
032 * @author <a href="mailto:arminw@apache.org">Armin Waibel</a>
033 * @version $Id: MaterializationCache.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
034 */
035 public class MaterializationCache implements ObjectCacheInternal
036 {
037 private static Logger log = LoggerFactory.getLogger(MaterializationCache.class);
038
039 private CacheDistributor cacheDistributor;
040 private HashMap objectBuffer;
041 private boolean enabledReadCache;
042 private int invokeCounter;
043
044 MaterializationCache(CacheDistributor cache)
045 {
046 this.cacheDistributor = cache;
047 this.objectBuffer = new HashMap();
048 enabledReadCache = false;
049 }
050
051 /**
052 * Returns <em>true</em> if the materialisation cache is enabled, otherwise <em>false</em>.
053 */
054 public boolean isEnabledMaterialisationCache()
055 {
056 return enabledReadCache;
057 }
058
059 /**
060 * For internal use only! Helper method to guarantee that only full materialized objects
061 * will be pushed to the application cache regardless if an local PB transaction
062 * is running or not. When a complex object is materialized there will be
063 * nested calls to the same PB instance methods, e.g. materialization of a referenced
064 * object which itself have several references, ...
065 * <br/>
066 * This method and {@link #disableMaterializationCache()} are used to delimit nested calls
067 * and to detect the end of an object materialization and avoid endless loops on circular
068 * references.
069 * <br/>
070 * If an code block with 'enabledMaterializationCache' throws an exception, in catch-block
071 * method {@link #doLocalClear()} have to be called.
072 */
073 public void enableMaterializationCache()
074 {
075 ++invokeCounter;
076 enabledReadCache = true;
077 }
078
079 /**
080 * @see #enableMaterializationCache()
081 */
082 public void disableMaterializationCache()
083 {
084 if(!enabledReadCache) return;
085
086 --invokeCounter;
087 /*
088 if materialization of the requested object was completed, the
089 counter represents '0' and we push the object
090 to the application cache
091 */
092 if(invokeCounter == 0)
093 {
094 try
095 {
096 if(log.isDebugEnabled())
097 {
098 log.debug("Materialisation of object is finished, push "
099 + objectBuffer.size() + "objects to cache");
100 }
101 pushObjects();
102 }
103 finally
104 {
105 doLocalClear();
106 }
107 }
108 }
109
110 public void doInternalCache(Identity oid, Object obj, int type)
111 {
112 // if OJB try to build an object graph put objects in local cache
113 // else use the application cache
114 if(enabledReadCache)
115 {
116 doLocalCache(oid, obj, type);
117 }
118 else
119 {
120 cacheDistributor.doInternalCache(oid, obj, type);
121 }
122 }
123
124 public void cache(Identity oid, Object obj)
125 {
126 doInternalCache(oid, obj, TYPE_UNKNOWN);
127 }
128
129 /**
130 * @see ObjectCacheInternal#cacheIfNew(org.apache.ojb.broker.Identity, Object)
131 */
132 public boolean cacheIfNew(Identity oid, Object obj)
133 {
134 boolean result = cacheDistributor.cacheIfNew(oid, obj);
135 if(enabledReadCache)
136 {
137 doLocalCache(oid, obj, TYPE_CACHED_READ);
138 }
139 return result;
140 }
141
142 public Object lookup(Identity oid)
143 {
144 Object result = null;
145 if(enabledReadCache)
146 {
147 result = doLocalLookup(oid);
148 }
149 if(result == null)
150 {
151 result = cacheDistributor.lookup(oid);
152 }
153 return result;
154 }
155
156 public Object doLocalLookup(Identity oid)
157 {
158 ObjectEntry entry = (ObjectEntry) objectBuffer.get(oid);
159 return entry != null ? entry.obj : null;
160 }
161
162 public void remove(Identity oid)
163 {
164 doLocalRemove(oid);
165 cacheDistributor.remove(oid);
166 }
167
168 public void doLocalRemove(Identity oid)
169 {
170 objectBuffer.remove(oid);
171 }
172
173 /**
174 * Clears the internal used cache for object materialization.
175 */
176 public void doLocalClear()
177 {
178 if(log.isDebugEnabled()) log.debug("Clear materialization cache");
179 invokeCounter = 0;
180 enabledReadCache = false;
181 objectBuffer.clear();
182 }
183
184 public void clear()
185 {
186 if(log.isDebugEnabled()) log.debug("Clear used caches");
187 doLocalClear();
188 cacheDistributor.clear();
189 }
190
191 private void doLocalCache(Identity oid, Object obj, int type)
192 {
193 objectBuffer.put(oid, new ObjectEntry(obj, type));
194 }
195
196 private void pushObjects()
197 {
198 Iterator it = objectBuffer.entrySet().iterator();
199 Map.Entry entry;
200 ObjectEntry oe;
201 while(it.hasNext())
202 {
203 entry = (Map.Entry) it.next();
204 oe = (ObjectEntry) entry.getValue();
205 /*
206 never push temporary object to a higher level cache
207 */
208 if(oe.type != TYPE_TEMP)
209 {
210 if(log.isDebugEnabled()) log.debug("Push to cache: " + entry.getKey());
211 cacheDistributor.doInternalCache((Identity) entry.getKey(), oe.obj, oe.type);
212 }
213 }
214 }
215
216 //===========================================================
217 // inner class
218 //===========================================================
219
220 static final class ObjectEntry
221 {
222 Object obj;
223 int type;
224
225 public ObjectEntry(Object obj, int type)
226 {
227 this.obj = obj;
228 this.type = type;
229 }
230 }
231 }