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 }