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 }