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 org.apache.commons.lang.SerializationUtils;
019    import org.apache.commons.lang.builder.ToStringBuilder;
020    import org.apache.commons.lang.builder.ToStringStyle;
021    import org.apache.ojb.broker.Identity;
022    import org.apache.ojb.broker.PBKey;
023    import org.apache.ojb.broker.PersistenceBroker;
024    import org.apache.ojb.broker.PersistenceBrokerFactory;
025    import org.apache.ojb.broker.util.collections.ManageableArrayList;
026    import org.apache.ojb.broker.util.configuration.Configuration;
027    import org.apache.ojb.broker.util.configuration.ConfigurationException;
028    import org.apache.ojb.broker.util.configuration.Configurator;
029    import org.apache.ojb.broker.util.factory.ConfigurableFactory;
030    import org.apache.ojb.broker.util.logging.Logger;
031    import org.apache.ojb.broker.util.logging.LoggerFactory;
032    import org.apache.ojb.odmg.locking.LockManager;
033    import org.apache.ojb.odmg.locking.LockManagerFactory;
034    import org.apache.ojb.odmg.oql.EnhancedOQLQuery;
035    import org.apache.ojb.odmg.oql.OQLQueryImpl;
036    import org.odmg.DArray;
037    import org.odmg.DBag;
038    import org.odmg.DList;
039    import org.odmg.DMap;
040    import org.odmg.DSet;
041    import org.odmg.Database;
042    import org.odmg.DatabaseClosedException;
043    import org.odmg.Implementation;
044    import org.odmg.ODMGRuntimeException;
045    import org.odmg.Transaction;
046    
047    
048    /**
049     * Default implementation of the {@link Implementation} interface.
050     *
051     * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
052     * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
053     * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
054     *
055     * @version $Id: ImplementationImpl.java,v 1.1 2007-08-24 22:17:37 ewestfal Exp $
056     */
057    public class ImplementationImpl implements ImplementationExt
058    {
059        private Logger log = LoggerFactory.getLogger(ImplementationImpl.class);
060    
061    //    private List usedDatabases = new ArrayList();
062        private DatabaseImpl currentDatabase;
063        private Configurator configurator;
064        private OJBTxManager ojbTxManager;
065        private LockManager lockManager;
066    
067        private Class oqlCollectionClass;
068        private boolean impliciteWriteLocks;
069        private boolean implicitLocking;
070        private boolean implicitLockingBackward;
071        private boolean ordering;
072    //    private boolean noteUserOrder;
073    
074        /**
075         * private Constructor: use static factory method
076         * getInstance() to obtain an instance
077         */
078        protected ImplementationImpl()
079        {
080            ojbTxManager = TxManagerFactory.instance();
081            lockManager = LockManagerFactory.getLockManager();
082            setConfigurator(PersistenceBrokerFactory.getConfigurator());
083            Configuration conf = getConfigurator().getConfigurationFor(null);
084            oqlCollectionClass = conf.getClass("OqlCollectionClass", ManageableArrayList.class);
085            impliciteWriteLocks = (conf.getString("LockAssociations", "WRITE").equalsIgnoreCase("WRITE"));
086            implicitLocking = conf.getBoolean("ImplicitLocking", true);
087            ordering = conf.getBoolean("Ordering", true);
088    //        noteUserOrder = conf.getBoolean("NoteUserOrder", true);
089            implicitLockingBackward = conf.getBoolean("ImplicitLockingBackward", false);
090            if(log.isEnabledFor(Logger.INFO))
091            {
092                log.info("Settings: " + this.toString());
093            }
094        }
095    
096        public OJBTxManager getTxManager()
097        {
098            return ojbTxManager;
099        }
100    
101        protected LockManager getLockManager()
102        {
103            return lockManager;
104        }
105    
106        protected synchronized void setCurrentDatabase(DatabaseImpl curDB)
107        {
108            currentDatabase = curDB;
109        }
110    
111        protected synchronized DatabaseImpl getCurrentDatabase()
112        {
113            return currentDatabase;
114        }
115    
116        public PBKey getCurrentPBKey()
117        {
118            return currentDatabase.getPBKey();
119        }
120    
121        /**
122         * Gets the configurator.
123         * @return Returns a Configurator
124         */
125        public Configurator getConfigurator()
126        {
127            return configurator;
128        }
129    
130        /**
131         * Sets the configurator.
132         * @param configurator The configurator to set
133         */
134        public void setConfigurator(Configurator configurator)
135        {
136            this.configurator = configurator;
137        }
138    
139        /**
140         * Create a <code>Transaction</code> object and associate it with the current thread.
141         * @return The newly created <code>Transaction</code> instance.
142         * @see Transaction
143         */
144        public Transaction newTransaction()
145        {
146            if ((getCurrentDatabase() == null))
147            {
148                throw new DatabaseClosedException("Database is NULL, must have a DB in order to create a transaction");
149            }
150            TransactionImpl tx = new TransactionImpl(this);
151            try
152            {
153                getConfigurator().configure(tx);
154            }
155            catch (ConfigurationException e)
156            {
157                throw new ODMGRuntimeException("Error in configuration of TransactionImpl instance: " + e.getMessage());
158            }
159            return tx;
160        }
161    
162        /**
163         * Get the current <code>Transaction</code> for the thread.
164         * @return The current <code>Transaction</code> object or null if there is none.
165         * @see Transaction
166         */
167        public Transaction currentTransaction()
168        {
169            if ((getCurrentDatabase() == null))
170            {
171                throw new DatabaseClosedException("Database is NULL, must have a DB in order to create a transaction");
172            }
173            return ojbTxManager.getTransaction();
174        }
175    
176        public boolean hasOpenTransaction()
177        {
178            TransactionImpl tx = ojbTxManager.getTransaction();
179            return tx != null && tx.isOpen();
180        }
181    
182        /**
183         * Create a new <code>Database</code> object.
184         * @return The new <code>Database</code> object.
185         * @see Database
186         */
187        public Database newDatabase()
188        {
189            return new DatabaseImpl(this);
190        }
191    
192        /**
193         * Create a new <code>OQLQuery</code> object.
194         * @return The new <code>OQLQuery</code> object.
195         * @see org.odmg.OQLQuery
196         */
197        public EnhancedOQLQuery newOQLQuery()
198        {
199            if ((getCurrentDatabase() == null) || !getCurrentDatabase().isOpen())
200            {
201                throw new DatabaseClosedException("Database is not open");
202            }
203            return new OQLQueryImpl(this);
204        }
205    
206        /**
207         * Create a new <code>DList</code> object.
208         * @return The new <code>DList</code> object.
209         * @see DList
210         */
211        public DList newDList()
212        {
213            if ((getCurrentDatabase() == null))
214            {
215                throw new DatabaseClosedException("Database is NULL, cannot create a DList with a null database.");
216            }
217            return (DList) DListFactory.singleton.createCollectionOrMap(getCurrentPBKey());
218        }
219    
220        /**
221         * Create a new <code>DBag</code> object.
222         * @return The new <code>DBag</code> object.
223         * @see DBag
224         */
225        public DBag newDBag()
226        {
227            if ((getCurrentDatabase() == null))
228            {
229                throw new DatabaseClosedException("Database is NULL, cannot create a DBag with a null database.");
230            }
231            return (DBag) DBagFactory.singleton.createCollectionOrMap(getCurrentPBKey());
232        }
233    
234        /**
235         * Create a new <code>DSet</code> object.
236         * @return The new <code>DSet</code> object.
237         * @see DSet
238         */
239        public DSet newDSet()
240        {
241            if ((getCurrentDatabase() == null))
242            {
243                throw new DatabaseClosedException("Database is NULL, cannot create a DSet with a null database.");
244            }
245            return (DSet) DSetFactory.singleton.createCollectionOrMap(getCurrentPBKey());
246        }
247    
248        /**
249         * Create a new <code>DArray</code> object.
250         * @return The new <code>DArray</code> object.
251         * @see DArray
252         */
253        public DArray newDArray()
254        {
255            if ((getCurrentDatabase() == null))
256            {
257                throw new DatabaseClosedException("Database is NULL, cannot create a DArray with a null database.");
258            }
259            return (DArray) DArrayFactory.singleton.createCollectionOrMap(getCurrentPBKey());
260        }
261    
262        /**
263         * Create a new <code>DMap</code> object.
264         * @return The new <code>DMap</code> object.
265         * @see DMap
266         */
267        public DMap newDMap()
268        {
269            if ((getCurrentDatabase() == null))
270            {
271                throw new DatabaseClosedException("Database is NULL, cannot create a DMap with a null database.");
272            }
273            return (DMap) DMapFactory.singleton.createCollectionOrMap(getCurrentPBKey());
274        }
275    
276        /**
277         * Get a <code>String</code> representation of the object's identifier.
278         * OJB returns the serialized Identity of the object.
279         * @param obj The object whose identifier is being accessed.
280         * @return The object's identifier in the form of a String
281         */
282        public String getObjectId(Object obj)
283        {
284            Identity oid = null;
285            PersistenceBroker broker = null;
286    
287            try
288            {
289                if (getCurrentDatabase() != null)
290                {
291                    /**
292                     * is there an open database we are calling getObjectId against? if yes, use it
293                     */
294                    broker = PersistenceBrokerFactory.createPersistenceBroker(getCurrentDatabase().getPBKey());
295                }
296                else
297                {
298                    log.warn("Can't find open database, try to use the default configuration");
299                    /**
300                     * otherwise, use default.
301                     */
302                    broker = PersistenceBrokerFactory.defaultPersistenceBroker();
303                }
304    
305                oid = broker.serviceIdentity().buildIdentity(obj);
306            }
307            finally
308            {
309                if(broker != null)
310                {
311                    broker.close();
312                }
313            }
314            return new String(SerializationUtils.serialize(oid));
315        }
316    
317        /**
318         * Returns the current used database or null.
319         */
320        public Database getDatabase(Object obj)
321        {
322            /* @todo enhance functionality */
323            return getCurrentDatabase();
324        }
325    
326        /**
327         * Register opened database via the PBKey.
328         */
329        protected synchronized void registerOpenDatabase(DatabaseImpl newDB)
330        {
331            DatabaseImpl old_db = getCurrentDatabase();
332            if (old_db != null)
333            {
334                try
335                {
336                    if (old_db.isOpen())
337                    {
338                        log.warn("## There is still an opened database, close old one ##");
339                        old_db.close();
340                    }
341                }
342                catch (Throwable t)
343                {
344                    //ignore
345                }
346            }
347            if (log.isDebugEnabled()) log.debug("Set current database " + newDB + " PBKey was " + newDB.getPBKey());
348            setCurrentDatabase(newDB);
349    //        usedDatabases.add(newDB.getPBKey());
350        }
351    
352        /**
353         * <strong>Note:</strong> Method behavior changed between version 1.0.3 and
354         * 1.0.4. Now this method is used to set the global property <em>implicit locking</em>,
355         * use method {@link TransactionExt#setImplicitLocking(boolean)} to set the property
356         * for a running transaction.
357         *
358         * @see ImplementationExt#setImplicitLocking(boolean)
359         */
360            public void setImplicitLocking(boolean value)
361            {
362            if(implicitLockingBackward)
363            {
364                ((TransactionExt)currentTransaction()).setImplicitLocking(value);
365            }
366            else
367            {
368                this.implicitLocking = value;
369            }
370            }
371    
372        /**
373         * @see ImplementationExt#isImplicitLocking()
374         */
375        public boolean isImplicitLocking()
376        {
377            return implicitLocking;
378        }
379    
380        /**
381         * @see ImplementationExt#getOqlCollectionClass()
382         */
383        public Class getOqlCollectionClass()
384        {
385            return oqlCollectionClass;
386        }
387    
388        /**
389         * @see ImplementationExt#setOqlCollectionClass(Class)
390         */
391        public void setOqlCollectionClass(Class oqlCollectionClass)
392        {
393            this.oqlCollectionClass = oqlCollectionClass;
394        }
395    
396        /**
397         * @see ImplementationExt#setImpliciteWriteLocks(boolean)
398         */
399        public void setImpliciteWriteLocks(boolean impliciteWriteLocks)
400        {
401            this.impliciteWriteLocks = impliciteWriteLocks;
402        }
403    
404        /**
405         * @see ImplementationExt#isImpliciteWriteLocks()
406         */
407        public boolean isImpliciteWriteLocks()
408        {
409            return impliciteWriteLocks;
410        }
411    
412        public boolean isOrdering()
413        {
414            return ordering;
415        }
416    
417        public void setOrdering(boolean ordering)
418        {
419            this.ordering = ordering;
420        }
421    
422    //    public boolean isNoteUserOrder()
423    //    {
424    //        return noteUserOrder;
425    //    }
426    //
427    //    public void setNoteUserOrder(boolean noteUserOrder)
428    //    {
429    //        this.noteUserOrder = noteUserOrder;
430    //    }
431    
432        /**
433         * Allow to use method {@link #setImplicitLocking(boolean)} in the same way
434         * as before version 1.0.4 - if set 'true', recommended setting is 'false'.
435         *
436         * @deprecated is only for backward compatibility with older versions (before 1.0.4)
437         * and will be removed in future versions.
438         */
439        public void setImplicitLockingBackward(boolean implicitLockingBackward)
440        {
441            this.implicitLockingBackward = implicitLockingBackward;
442        }
443    
444        public String toString()
445        {
446            return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
447                    .append("implicitLocking", isImplicitLocking())
448                    .append("implicitWriteLocks", isImpliciteWriteLocks())
449                    .append("ordering", isOrdering())
450                    .append("oqlCollectionClass", getOqlCollectionClass())
451                    .append("txManager", getTxManager())
452                    .append("lockManager", getLockManager())
453                    .toString();
454        }
455    
456    
457        //*****************************************************
458        // inner classes
459        //*****************************************************
460    
461        abstract static class BaseFactory extends ConfigurableFactory
462        {
463            Object createCollectionOrMap()
464            {
465                return this.createNewInstance();
466            }
467    
468            Object createCollectionOrMap(PBKey key)
469            {
470                return createNewInstance(PBKey.class, key);
471            }
472        }
473    
474        static final class DListFactory extends BaseFactory
475        {
476            static final BaseFactory singleton = new DListFactory();
477            protected String getConfigurationKey()
478            {
479                return "DListClass";
480            }
481        }
482    
483        static final class DArrayFactory extends BaseFactory
484        {
485            static final BaseFactory singleton = new DArrayFactory();
486            protected String getConfigurationKey()
487            {
488                return "DArrayClass";
489            }
490        }
491    
492        static final class DBagFactory extends BaseFactory
493        {
494            static final BaseFactory singleton = new DBagFactory();
495            protected String getConfigurationKey()
496            {
497                return "DBagClass";
498            }
499        }
500    
501        static final class DSetFactory extends BaseFactory
502        {
503            static final BaseFactory singleton = new DSetFactory();
504            protected String getConfigurationKey()
505            {
506                return "DSetClass";
507            }
508        }
509    
510        static final class DMapFactory extends BaseFactory
511        {
512            static final BaseFactory singleton = new DMapFactory();
513            protected String getConfigurationKey()
514            {
515                return "DMapClass";
516            }
517        }
518    }