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.ojb.broker.PBFactoryException;
019    import org.apache.ojb.broker.PBKey;
020    import org.apache.ojb.broker.PersistenceBroker;
021    import org.apache.ojb.broker.PersistenceBrokerFactory;
022    import org.apache.ojb.broker.util.BrokerHelper;
023    import org.apache.ojb.broker.util.logging.Logger;
024    import org.apache.ojb.broker.util.logging.LoggerFactory;
025    import org.odmg.DatabaseClosedException;
026    import org.odmg.DatabaseNotFoundException;
027    import org.odmg.DatabaseOpenException;
028    import org.odmg.ODMGException;
029    import org.odmg.ObjectNameNotFoundException;
030    import org.odmg.ObjectNameNotUniqueException;
031    import org.odmg.TransactionInProgressException;
032    import org.odmg.TransactionNotInProgressException;
033    
034    /**
035     * Implementation class of the {@link org.odmg.Database} interface.
036     * 
037     * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
038     * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
039     * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
040     * @version $Id: DatabaseImpl.java,v 1.1 2007-08-24 22:17:37 ewestfal Exp $
041     */
042    public class DatabaseImpl implements org.odmg.Database
043    {
044        private Logger log = LoggerFactory.getLogger(DatabaseImpl.class);
045    
046        private PBKey pbKey;
047        private boolean isOpen;
048        private ImplementationImpl odmg;
049    
050        public DatabaseImpl(ImplementationImpl ojb)
051        {
052            isOpen = false;
053            this.odmg = ojb;
054        }
055    
056        private TransactionImpl getTransaction()
057        {
058            Object result = odmg.currentTransaction();
059            // TODO: remove this workaround
060            // In managed environments only wrapped tx are returned, so
061            // we have to extract the real tx first
062            if(result instanceof NarrowTransaction)
063            {
064                return ((NarrowTransaction) result).getRealTransaction();
065            }
066            return (TransactionImpl) result;
067        }
068    
069        /**
070         * Return the {@link org.apache.ojb.broker.PBKey} associated with this Database.
071         */
072        public PBKey getPBKey()
073        {
074            if (pbKey == null)
075            {
076                log.error("## PBKey not set, Database isOpen=" + isOpen + " ##");
077                if (!isOpen) throw new DatabaseClosedException("Database is not open");
078            }
079            return pbKey;
080        }
081    
082        public boolean isOpen()
083        {
084            return this.isOpen;
085        }
086    
087        /**
088         * Open the named database with the specified access mode.
089         * Attempts to open a database when it has already been opened will result in
090         * the throwing of the exception <code>DatabaseOpenException</code>.
091         * A <code>DatabaseNotFoundException</code> is thrown if the database does not exist.
092         * Some implementations may throw additional exceptions that are also derived from
093         * <code>ODMGException</code>.
094         * @param name The name of the database.
095         * @param accessMode The access mode, which should be one of the static fields:
096         * <code>OPEN_READ_ONLY</code>, <code>OPEN_READ_WRITE</code>,
097         * or <code>OPEN_EXCLUSIVE</code>.
098         * @exception ODMGException The database could not be opened.
099         */
100        public synchronized void open(String name, int accessMode) throws ODMGException
101        {
102            if (isOpen())
103            {
104                throw new DatabaseOpenException("Database is already open");
105            }
106            PersistenceBroker broker = null;
107            try
108            {
109                if (name == null)
110                {
111                    log.info("Given argument was 'null', open default database");
112                    broker = PersistenceBrokerFactory.defaultPersistenceBroker();
113                }
114                else
115                {
116                    broker = PersistenceBrokerFactory.createPersistenceBroker(
117                            BrokerHelper.extractAllTokens(name));
118                }
119                pbKey = broker.getPBKey();
120                isOpen = true;
121                //register opened database
122                odmg.registerOpenDatabase(this);
123                if (log.isDebugEnabled()) log.debug("Open database using PBKey " + pbKey);
124            }
125            catch (PBFactoryException ex)
126            {
127                log.error("Open database failed: " + ex.getMessage(), ex);
128                throw new DatabaseNotFoundException(
129                        "OJB can't open database " + name + "\n" + ex.getMessage());
130            }
131            finally
132            {
133                // broker must be immediately closed
134                if (broker != null)
135                {
136                    broker.close();
137                }
138            }
139        }
140    
141        /**
142         * Close the database.
143         * After you have closed a database, further attempts to access objects in the
144         * database will cause the exception <code>DatabaseClosedException</code> to be thrown.
145         * Some implementations may throw additional exceptions that are also derived
146         * from <code>ODMGException</code>.
147         * @exception ODMGException Unable to close the database.
148         */
149        public void close() throws ODMGException
150        {
151            /**
152             * is the DB open? ODMG 3.0 says we can't close an already open database.
153             */
154            if (!isOpen())
155            {
156                throw new DatabaseClosedException("Database is not Open. Must have an open DB to call close.");
157            }
158            /**
159             * is the associated Tx open? ODMG 3.0 says we can't close the database with an open Tx pending.
160             * check if a tx was found, the tx was associated with database
161             */
162            if (odmg.hasOpenTransaction() &&
163                    getTransaction().getAssociatedDatabase().equals(this))
164            {
165                String msg = "Database cannot be closed, associated Tx is still open." +
166                        " Transaction status is '" + TxUtil.getStatusString(getTransaction().getStatus()) + "'." +
167                        " Used PBKey was "+getTransaction().getBroker().getPBKey();
168                log.error(msg);
169                throw new TransactionInProgressException(msg);
170            }
171            isOpen = false;
172            // remove the current PBKey
173            pbKey = null;
174            // if we close current database, we have to notify implementation instance
175            if (this == odmg.getCurrentDatabase())
176            {
177                odmg.setCurrentDatabase(null);
178            }
179        }
180    
181        /**
182         * Associate a name with an object and make it persistent.
183         * An object instance may be bound to more than one name.
184         * Binding a previously transient object to a name makes that object persistent.
185         * @param object The object to be named.
186         * @param name The name to be given to the object.
187         * @exception org.odmg.ObjectNameNotUniqueException
188         * If an attempt is made to bind a name to an object and that name is already bound
189         * to an object.
190         */
191        public void bind(Object object, String name)
192                throws ObjectNameNotUniqueException
193        {
194            /**
195             * Is DB open? ODMG 3.0 says it has to be to call bind.
196             */
197            if (!this.isOpen())
198            {
199                throw new DatabaseClosedException("Database is not open. Must have an open DB to call bind.");
200            }
201            /**
202             * Is Tx open? ODMG 3.0 says it has to be to call bind.
203             */
204            TransactionImpl tx = getTransaction();
205            if (tx == null || !tx.isOpen())
206            {
207                throw new TransactionNotInProgressException("Tx is not open. Must have an open TX to call bind.");
208            }
209    
210            tx.getNamedRootsMap().bind(object, name);
211        }
212    
213        /**
214         * Lookup an object via its name.
215         * @param name The name of an object.
216         * @return The object with that name.
217         * @exception ObjectNameNotFoundException There is no object with the specified name.
218         * ObjectNameNotFoundException
219         */
220        public Object lookup(String name) throws ObjectNameNotFoundException
221        {
222            /**
223             * Is DB open? ODMG 3.0 says it has to be to call bind.
224             */
225            if (!this.isOpen())
226            {
227                throw new DatabaseClosedException("Database is not open. Must have an open DB to call lookup");
228            }
229            /**
230             * Is Tx open? ODMG 3.0 says it has to be to call bind.
231             */
232            TransactionImpl tx = getTransaction();
233            if (tx == null || !tx.isOpen())
234            {
235                throw new TransactionNotInProgressException("Tx is not open. Must have an open TX to call lookup.");
236            }
237    
238            return tx.getNamedRootsMap().lookup(name);
239        }
240    
241        /**
242         * Disassociate a name with an object
243         * @param name The name of an object.
244         * @exception ObjectNameNotFoundException No object exists in the database with that name.
245         */
246        public void unbind(String name) throws ObjectNameNotFoundException
247        {
248            /**
249             * Is DB open? ODMG 3.0 says it has to be to call unbind.
250             */
251            if (!this.isOpen())
252            {
253                throw new DatabaseClosedException("Database is not open. Must have an open DB to call unbind");
254            }
255            TransactionImpl tx = getTransaction();
256            if (tx == null || !tx.isOpen())
257            {
258                throw new TransactionNotInProgressException("Tx is not open. Must have an open TX to call lookup.");
259            }
260    
261            tx.getNamedRootsMap().unbind(name);
262        }
263    
264        /**
265         * Make a transient object durable in the database.
266         * It must be executed in the context of an open transaction.
267         * If the transaction in which this method is executed commits,
268         * then the object is made durable.
269         * If the transaction aborts,
270         * then the makePersistent operation is considered not to have been executed,
271         * and the target object is again transient.
272         * ClassNotPersistenceCapableException is thrown if the implementation cannot make
273         * the object persistent because of the type of the object.
274         * @param   object  The object to make persistent.
275         * @throws TransactionNotInProgressException if there is no current transaction.
276         */
277        public void makePersistent(Object object)
278        {
279            /**
280             * Is DB open? ODMG 3.0 says it has to be to call makePersistent.
281             */
282            if (!this.isOpen())
283            {
284                throw new DatabaseClosedException("Database is not open");
285            }
286            /**
287             * Is Tx open? ODMG 3.0 says it has to be to call makePersistent.
288             */
289            TransactionImpl tx = getTransaction();
290            if (tx == null || !tx.isOpen())
291            {
292                throw new TransactionNotInProgressException("No transaction in progress, cannot persist");
293            }
294            RuntimeObject rt = new RuntimeObject(object, getTransaction());
295            tx.makePersistent(rt);
296    //        tx.moveToLastInOrderList(rt.getIdentity());
297        }
298    
299        /**
300         * Deletes an object from the database.
301         * It must be executed in the context of an open transaction.
302         * If the object is not persistent, then ObjectNotPersistent is thrown.
303         * If the transaction in which this method is executed commits,
304         * then the object is removed from the database.
305         * If the transaction aborts,
306         * then the deletePersistent operation is considered not to have been executed,
307         * and the target object is again in the database.
308         * @param   object  The object to delete.
309         */
310        public void deletePersistent(Object object)
311        {
312            if (!this.isOpen())
313            {
314                throw new DatabaseClosedException("Database is not open");
315            }
316            TransactionImpl tx = getTransaction();
317            if (tx == null || !tx.isOpen())
318            {
319                throw new TransactionNotInProgressException("No transaction in progress, cannot delete persistent");
320            }
321            RuntimeObject rt = new RuntimeObject(object, tx);
322            tx.deletePersistent(rt);
323    //        tx.moveToLastInOrderList(rt.getIdentity());
324        }
325    }