001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049:
050: package org.jaffa.persistence.engines.jdbcengine;
051:
052: import org.apache.log4j.Logger;
053: import java.util.*;
054: import org.jaffa.persistence.Criteria;
055: import org.jaffa.persistence.IPersistent;
056: import org.jaffa.persistence.engines.IPersistenceEngine;
057: import org.jaffa.persistence.engines.jdbcengine.configservice.ConfigurationService;
058: import org.jaffa.persistence.engines.jdbcengine.datasource.PersistentTransaction;
059: import org.jaffa.persistence.engines.jdbcengine.interceptor.AbstractInterceptor;
060: import org.jaffa.persistence.engines.jdbcengine.interceptor.AddInterceptor;
061: import org.jaffa.persistence.engines.jdbcengine.interceptor.LockInterceptor;
062: import org.jaffa.persistence.engines.jdbcengine.interceptor.UpdateInterceptor;
063: import org.jaffa.persistence.engines.jdbcengine.interceptor.DeleteInterceptor;
064: import org.jaffa.persistence.engines.jdbcengine.interceptor.QueryInterceptor;
065: import org.jaffa.persistence.engines.jdbcengine.querygenerator.JdbcBridge;
066: import org.jaffa.persistence.engines.jdbcengine.proxy.PersistentInstanceFactory;
067: import org.jaffa.persistence.exceptions.*;
068: import org.jaffa.persistence.engines.jdbcengine.datasource.exceptions.DataSourceCreationException;
069: import java.sql.SQLException;
070: import org.jaffa.persistence.engines.jdbcengine.configservice.initdomain.Database;
071:
072: /** An implementation of the IPersistenceEngine interface. This uses the JDBC API for persistence. */
073: public class Engine implements IPersistenceEngine {
074:
075: private static final Logger log = Logger.getLogger(Engine.class);
076: private static final String DEFAULT_DATA_SOURCE_NAME = "default";
077:
078: // this will be the 1st interceptor in the chain.
079: private static AbstractInterceptor c_interceptor = null;
080:
081: static {
082: if (log.isDebugEnabled())
083: log.debug("Initializing the ConfigurationService");
084: ConfigurationService.getInstance();
085:
086: // create the Interceptor chain
087: createInterceptorChain();
088: }
089:
090: // The PersistentTransaction object associated with an Engine, which in turn is associated to an UOW
091: private PersistentTransaction m_pt = null;
092:
093: /** Creates new Engine. This will acquire a connection to the database.
094: * @throws DataSourceCreationException if the connection could not be acquired.
095: */
096: public Engine() throws DataSourceCreationException {
097: if (log.isDebugEnabled())
098: log.debug("Acquiring the Database configuration for: "
099: + DEFAULT_DATA_SOURCE_NAME);
100: Database database = ConfigurationService.getInstance()
101: .getDatabase(DEFAULT_DATA_SOURCE_NAME);
102:
103: if (log.isDebugEnabled())
104: log
105: .debug("Creating a PersistentTransaction for the database: "
106: + DEFAULT_DATA_SOURCE_NAME);
107: m_pt = new PersistentTransaction(database);
108: }
109:
110: /** Generates an appropriate instance for the input persistentClass.
111: * If the persistentClass is a 'Class', then it should implement the 'IPersistent' interface. The persistence engine will simply instantiate the class.
112: * If the persistentClass is an 'Interface', then the persistence engine will generate a dynamic proxy, to implement the IPersistent and the 'persistentClass' interfaces.
113: * @param persistentClass The actual persistentClass which can represent a 'Class' or an 'Interface'
114: * @return an instance implementing the IPersistent interface.
115: */
116: public IPersistent newPersistentInstance(Class persistentClass) {
117: return PersistentInstanceFactory
118: .newPersistentInstance(persistentClass);
119: }
120:
121: /** This is a helper method to determine the actual class which was used to create an IPersistent instance.
122: * It is quite possible that the input object is a dynamic proxy.
123: * @param persistentObject The object which implements the IPersistent instance.
124: * @return The class which was used for instantiating the instance.
125: */
126: public Class getActualPersistentClass(Object persistentObject) {
127: return PersistentInstanceFactory
128: .getActualPersistentClass((IPersistent) persistentObject);
129: }
130:
131: /**
132: * Adds an object to the persistent store.
133: * The object(s) will be added only on a <code>commit</code>.
134: * @param object The object to persist.
135: * @throws AddFailedException if any error occurs during the process.
136: */
137: public void add(IPersistent object) throws AddFailedException {
138: if (log.isDebugEnabled())
139: log.debug("Invoking the add on the PersistentTransaction");
140: m_pt.addObject(object);
141: }
142:
143: /**
144: * Updates an object from the persistent store.
145: * The object(s) will be updated only on a <code>commit</code>.
146: * @param object The object to update.
147: * @throws UpdateFailedException if any error occurs during the process.
148: */
149: public void update(IPersistent object) throws UpdateFailedException {
150: if (log.isDebugEnabled())
151: log
152: .debug("Invoking the update on the PersistentTransaction");
153: m_pt.updateObject(object);
154: }
155:
156: /**
157: * Deletes an object from the persistent store.
158: * The object(s) will be deleted only on a <code>commit</code>.
159: * @param object The object to delete from persistent storage.
160: * @throws DeleteFailedException if any error occurs during the process.
161: */
162: public void delete(IPersistent object) throws DeleteFailedException {
163: if (log.isDebugEnabled())
164: log
165: .debug("Invoking the delete on the PersistentTransaction");
166: m_pt.deleteObject(object);
167: }
168:
169: /**
170: * Queries the underlying persistent store based on the search profile passed in the Criteria object.
171: * This will execute the Add, Update, Delete interceptors before performing the query.
172: * @param criteria search profile for the query.
173: * @return a Collection of persistent objects.
174: * @throws QueryFailedException if any error occurs during the process.
175: * @throws PostLoadFailedException if any error occurs during the invocation of the PostLoad trigger on the persistent object.
176: */
177: public Collection query(Criteria criteria)
178: throws QueryFailedException, PostLoadFailedException {
179: if (log.isDebugEnabled())
180: log
181: .debug("Invoking the query on the PersistentTransaction");
182: m_pt.queryObject(criteria);
183:
184: try {
185: if (log.isDebugEnabled())
186: log.debug("Initiating the Interceptor chain with "
187: + c_interceptor.getClass().getName());
188: return (Collection) c_interceptor.invoke(m_pt);
189: } catch (QueryFailedException e) {
190: throw e;
191: } catch (PostLoadFailedException e) {
192: throw e;
193: } catch (UOWException e) {
194: // wrap all other uow exceptions inside the QueryFailedException
195: throw new QueryFailedException(null, e);
196: }
197: }
198:
199: /**
200: * Objects that have been added, objects that have been deleted,
201: * and objects that have been updated, will all be persisted via
202: * an invocation of this method.
203: * @throws AddFailedException if any error occurs during the addition of objects to the persistent store.
204: * @throws UpdateFailedException if any error occurs while updating the objects of the persistent store.
205: * @throws DeleteFailedException if any error occurs while deleting the objects of the persistent store.
206: * @throws CommitFailedException if any error occurs during the commit.
207: */
208: public void commit() throws AddFailedException,
209: UpdateFailedException, DeleteFailedException,
210: CommitFailedException {
211: try {
212: if (log.isDebugEnabled())
213: log.debug("Initiating the Interceptor chain with "
214: + c_interceptor.getClass().getName());
215: c_interceptor.invoke(m_pt);
216:
217: if (log.isDebugEnabled())
218: log
219: .debug("Invoking the commit on the PersistentTransaction");
220: m_pt.commit();
221: } catch (AddFailedException e) {
222: throw e;
223: } catch (UpdateFailedException e) {
224: throw e;
225: } catch (DeleteFailedException e) {
226: throw e;
227: } catch (CommitFailedException e) {
228: throw e;
229: } catch (UOWException e) {
230: // wrap all other uow exceptions inside the CommitFailedException
231: throw new CommitFailedException(null, e);
232: } catch (SQLException e) {
233: String str = "Error in committing the transaction";
234: log.error(str, e);
235: throw new CommitFailedException(null, e);
236: }
237: }
238:
239: /**
240: * Rollbacks all the additions, deletions, updations.
241: * @throws RollbackFailedException if any error occurs during the process.
242: */
243: public void rollback() throws RollbackFailedException {
244: try {
245: if (log.isDebugEnabled())
246: log
247: .debug("Invoking the rollback on the PersistentTransaction");
248: m_pt.rollback();
249: } catch (SQLException e) {
250: String str = "Error in executing a rollback on the transaction";
251: log.error(str, e);
252: throw new RollbackFailedException(null, e);
253: }
254: }
255:
256: /** Frees up the connection to the database. */
257: public void close() {
258: try {
259: if (log.isDebugEnabled())
260: log
261: .debug("Invoking the close on the PersistentTransaction");
262: m_pt.close();
263: } catch (SQLException e) {
264: String str = "Error in closing the transaction. But continuing with execution.";
265: log.error(str, e);
266: }
267: }
268:
269: /**
270: * This will acquire a lock on the database row corrsponding to the input persistent object.
271: * @param object The persistent object to be locked.
272: * @throws AlreadyLockedObjectException if the database row has been locked by another process.
273: */
274: public void acquireLock(IPersistent object)
275: throws AlreadyLockedObjectException {
276: try {
277: if (object.isLocked()) {
278: if (log.isDebugEnabled())
279: log.debug("Already locked object " + object);
280: } else {
281: if (log.isDebugEnabled())
282: log
283: .debug("Invoking JdbcBridge.executeLock() for the object "
284: + object);
285: JdbcBridge.executeLock(object, m_pt.getDataSource());
286: }
287: } catch (Exception e) {
288: String str = "Error while locking the Persistent object in the database: "
289: + object;
290: log.error(str, e);
291: throw new AlreadyLockedObjectException(
292: new Object[] { object.getClass().getName() }, e);
293: }
294: }
295:
296: /** Creates an InterceptorChain and assign the start of the chain to the static c_interceptor.
297: */
298: private static void createInterceptorChain() {
299: AbstractInterceptor[] interceptors = new AbstractInterceptor[] {
300: new AddInterceptor(), new LockInterceptor(),
301: new UpdateInterceptor(), new DeleteInterceptor(),
302: new QueryInterceptor() };
303:
304: if (log.isDebugEnabled())
305: log.debug("Creating the Interceptor chain");
306:
307: AbstractInterceptor previousInterceptor = null;
308: for (int i = 0; i < interceptors.length; i++) {
309: if (previousInterceptor == null) {
310: // 1st interceptor of the chain
311: previousInterceptor = interceptors[i];
312: c_interceptor = interceptors[i];
313: } else {
314: previousInterceptor.setNextInterceptor(interceptors[i]);
315: previousInterceptor = interceptors[i];
316: }
317: }
318:
319: if (log.isDebugEnabled()) {
320: StringBuffer buf = new StringBuffer(
321: "Created the interceptor chain:\n");
322: AbstractInterceptor interceptor = c_interceptor;
323: boolean firstPass = true;
324: while (interceptor != null) {
325: if (firstPass)
326: firstPass = false;
327: else
328: buf.append(" --> ");
329: buf.append(interceptor.getClass().getName());
330: interceptor = interceptor.getNextInterceptor();
331: }
332: log.debug(buf.toString());
333: }
334: }
335:
336: }
|