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    }