001 package org.apache.ojb.odmg; 002 003 /* Copyright 2002-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.io.Serializable; 019 import java.util.HashMap; 020 import java.util.Iterator; 021 import java.util.Map; 022 023 import org.apache.commons.lang.SerializationUtils; 024 import org.apache.ojb.broker.Identity; 025 import org.apache.ojb.broker.PersistenceBroker; 026 import org.apache.ojb.broker.PersistenceBrokerException; 027 import org.apache.ojb.broker.core.proxy.ProxyHelper; 028 import org.apache.ojb.broker.metadata.ClassDescriptor; 029 import org.apache.ojb.broker.util.ObjectModification; 030 import org.apache.ojb.broker.util.logging.Logger; 031 import org.apache.ojb.broker.util.logging.LoggerFactory; 032 import org.odmg.ClassNotPersistenceCapableException; 033 import org.odmg.ObjectNameNotFoundException; 034 import org.odmg.ObjectNameNotUniqueException; 035 import org.odmg.Transaction; 036 037 /** 038 * ODMG NamedRoots implementation. 039 * this implementation stores the (name, Identity) pairs in 040 * a database table. 041 * therefore the NamedRootsMap underlies the same transaction management 042 * as all other persistent objects 043 * 044 * @author Thomas Mahler 045 * @version $Id: NamedRootsMap.java,v 1.1 2007-08-24 22:17:37 ewestfal Exp $ 046 */ 047 public class NamedRootsMap 048 { 049 private Logger log = LoggerFactory.getLogger(NamedRootsMap.class); 050 private TransactionImpl tx; 051 private HashMap tempBindings; 052 private Map deletionMap; 053 private Map insertMap; 054 055 NamedRootsMap(TransactionImpl tx) 056 { 057 this.tx = tx; 058 this.tempBindings = new HashMap(); 059 } 060 061 private void addForDeletion(NamedEntry entry) 062 { 063 if(deletionMap == null) 064 { 065 deletionMap = new HashMap(); 066 } 067 deletionMap.put(entry.getName(), entry); 068 } 069 070 private void addForInsert(NamedEntry entry) 071 { 072 if(insertMap == null) 073 { 074 insertMap = new HashMap(); 075 } 076 insertMap.put(entry.getName(), entry); 077 if(deletionMap != null) deletionMap.remove(entry.getName()); 078 } 079 080 /** 081 * Have to be performed after the "normal" objects be written 082 * to DB and before method {@link #performInsert()}. 083 */ 084 public void performDeletion() 085 { 086 if(deletionMap == null) 087 return; 088 else 089 { 090 PersistenceBroker broker = tx.getBroker(); 091 Iterator it = deletionMap.values().iterator(); 092 while(it.hasNext()) 093 { 094 NamedEntry namedEntry = (NamedEntry) it.next(); 095 broker.delete(namedEntry); 096 } 097 } 098 } 099 100 /** 101 * Have to be performed after the "normal" objects be written 102 * to DB and after method {@link #performDeletion()}. 103 */ 104 public void performInsert() 105 { 106 if(insertMap == null) 107 return; 108 else 109 { 110 PersistenceBroker broker = tx.getBroker(); 111 Iterator it = insertMap.values().iterator(); 112 while(it.hasNext()) 113 { 114 NamedEntry namedEntry = (NamedEntry) it.next(); 115 namedEntry.prepareForStore(broker); 116 broker.store(namedEntry, ObjectModification.INSERT); 117 } 118 } 119 } 120 121 public void afterWriteCleanup() 122 { 123 if(deletionMap != null) deletionMap.clear(); 124 if(insertMap != null) insertMap.clear(); 125 } 126 127 private void localBind(String key, NamedEntry entry) throws ObjectNameNotUniqueException 128 { 129 if(tempBindings.containsKey(key)) 130 { 131 throw new ObjectNameNotUniqueException("Object key already in use, the key '" 132 + key + "' is not unique"); 133 } 134 else 135 { 136 tempBindings.put(key, entry); 137 } 138 } 139 140 private void localUnbind(String key) 141 { 142 tempBindings.remove(key); 143 } 144 145 private NamedEntry localLookup(String key) 146 { 147 return (NamedEntry) tempBindings.get(key); 148 } 149 150 /** 151 * Return a named object associated with the specified key. 152 */ 153 Object lookup(String key) throws ObjectNameNotFoundException 154 { 155 Object result = null; 156 NamedEntry entry = localLookup(key); 157 // can't find local bound object 158 if(entry == null) 159 { 160 try 161 { 162 PersistenceBroker broker = tx.getBroker(); 163 // build Identity to lookup entry 164 Identity oid = broker.serviceIdentity().buildIdentity(NamedEntry.class, key); 165 entry = (NamedEntry) broker.getObjectByIdentity(oid); 166 } 167 catch(Exception e) 168 { 169 log.error("Can't materialize bound object for key '" + key + "'", e); 170 } 171 } 172 if(entry == null) 173 { 174 log.info("No object found for key '" + key + "'"); 175 } 176 else 177 { 178 Object obj = entry.getObject(); 179 // found a persistent capable object associated with that key 180 if(obj instanceof Identity) 181 { 182 Identity objectIdentity = (Identity) obj; 183 result = tx.getBroker().getObjectByIdentity(objectIdentity); 184 // lock the persistance capable object 185 RuntimeObject rt = new RuntimeObject(result, objectIdentity, tx, false); 186 tx.lockAndRegister(rt, Transaction.READ, tx.getRegistrationList()); 187 } 188 else 189 { 190 // nothing else to do 191 result = obj; 192 } 193 } 194 if(result == null) throw new ObjectNameNotFoundException("Can't find named object for name '" + key + "'"); 195 return result; 196 } 197 198 /** 199 * Remove a named object 200 */ 201 void unbind(String key) 202 { 203 NamedEntry entry = new NamedEntry(key, null, false); 204 localUnbind(key); 205 addForDeletion(entry); 206 } 207 208 public void bind(Object object, String name) throws ObjectNameNotUniqueException 209 { 210 boolean useIdentity = true; 211 PersistenceBroker broker = tx.getBroker(); 212 ClassDescriptor cld = null; 213 try 214 { 215 cld = broker.getClassDescriptor(ProxyHelper.getRealClass(object)); 216 } 217 catch(PersistenceBrokerException e) 218 { 219 } 220 221 // if null a non-persistent capable object was specified 222 if(cld == null) 223 { 224 useIdentity = false; 225 if(!(object instanceof Serializable)) 226 { 227 throw new ClassNotPersistenceCapableException( 228 "Can't bind named object, because it's not Serializable. Name=" + name + ", object=" + object); 229 } 230 } 231 else 232 { 233 RuntimeObject rt = new RuntimeObject(object, tx); 234 // if the object is already persistet, check for read 235 // lock to make sure 236 // that the used object is a valid version 237 // else persist the specified named object 238 if(!rt.isNew()) 239 { 240 tx.lockAndRegister(rt, Transaction.READ, tx.getRegistrationList()); 241 } 242 else 243 { 244 tx.makePersistent(rt); 245 } 246 } 247 NamedEntry oldEntry = localLookup(name); 248 if(oldEntry == null) 249 { 250 Identity oid = broker.serviceIdentity().buildIdentity(NamedEntry.class, name); 251 oldEntry = (NamedEntry) broker.getObjectByIdentity(oid); 252 } 253 if(oldEntry != null) 254 { 255 throw new ObjectNameNotUniqueException("The name of the specified named object already exist, name=" + name); 256 } 257 258 NamedEntry entry = new NamedEntry(name, object, useIdentity); 259 addForInsert(entry); 260 localBind(name, entry); 261 } 262 263 264 265 //============================================== 266 // inner class 267 //============================================== 268 /** 269 * represents an entry to the named roots table. 270 * maps names (Strings) to OJB Identities 271 */ 272 public static final class NamedEntry implements Serializable 273 { 274 static final long serialVersionUID = 6179717896336300342L; 275 /** 276 * the name under which an object is registered in the NamedRoots Map 277 */ 278 private String name; 279 /** 280 * the serialized Identity representing the named Object 281 */ 282 private byte[] oid; 283 284 private transient Object object; 285 private transient boolean useIdentity; 286 287 public NamedEntry() 288 { 289 } 290 291 NamedEntry(final String aName, final Object object, final boolean useIdentity) 292 { 293 this.name = aName; 294 this.object = object; 295 this.useIdentity = useIdentity; 296 } 297 298 /** 299 * This has to be called before this object will be persistet. 300 */ 301 public void prepareForStore(PersistenceBroker broker) 302 { 303 if(object != null) 304 { 305 if(useIdentity) 306 { 307 Identity oid = broker.serviceIdentity().buildIdentity(object); 308 this.oid = SerializationUtils.serialize(oid); 309 } 310 else 311 { 312 this.oid = SerializationUtils.serialize((Serializable) object); 313 } 314 } 315 } 316 317 public String getName() 318 { 319 return name; 320 } 321 322 public void setName(String name) 323 { 324 this.name = name; 325 } 326 327 public byte[] getOid() 328 { 329 return oid; 330 } 331 332 public void setOid(byte[] oid) 333 { 334 this.oid = oid; 335 } 336 337 Object getObject() 338 { 339 if(object != null) 340 { 341 return object; 342 } 343 else 344 { 345 return oid != null ? SerializationUtils.deserialize(oid) : null; 346 } 347 } 348 } 349 }