001: /**********************************************************************
002: Copyright (c) 2007 Erik Bengtson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015: Contributors:
016: 2007 Andy Jefferson - javadocs, formatted, copyrighted
017: 2007 Andy Jefferson - added lock/unlock/hasConnection/hasLockedConnection and enlisting
018: ...
019: **********************************************************************/package org.jpox;
020:
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import javax.transaction.xa.XAResource;
025:
026: import org.jpox.exceptions.JPOXUserException;
027: import org.jpox.management.ManagementServer;
028: import org.jpox.management.runtime.ConnectionManagerRuntime;
029: import org.jpox.transaction.Transaction;
030: import org.jpox.util.ClassUtils;
031: import org.jpox.util.JPOXLogger;
032: import org.jpox.util.Localiser;
033:
034: /**
035: * Manager of connections for an OMF, allowing ManagedConnection pooling, enlistment in transaction.
036: * The pool caches one connection per ObjectManager.
037: * The "allocate" method can create connections and enlist them (like most normal persistence operations need)
038: * or create a connection and return it.
039: *
040: * @version $Revision: 1.17 $
041: */
042: public class ConnectionManagerImpl implements ConnectionManager {
043: /** Localisation of messages. */
044: protected static final Localiser LOCALISER = Localiser
045: .getInstance("org.jpox.Localisation");
046:
047: /** OMFContext for this connection manager. */
048: OMFContext omfContext;
049:
050: ManagedConnectionPool connectionPool = new ManagedConnectionPool();
051:
052: /** Connection Runtime. Used when providing management of services. */
053: ConnectionManagerRuntime connMgrRuntime = null;
054:
055: /**
056: * Constructor.
057: * @param omfContext OMFContext for this manager.
058: */
059: public ConnectionManagerImpl(OMFContext omfContext) {
060: this .omfContext = omfContext;
061:
062: if (omfContext.getManagement() != null) {
063: // register MBean in MbeanServer
064: ManagementServer mgmtServer = this .omfContext
065: .getManagement().getManagementServer();
066: connMgrRuntime = new ConnectionManagerRuntime();
067: String mbeanName = omfContext.getDomainName()
068: + ":InstanceName="
069: + omfContext.getInstanceName()
070: + ",Type="
071: + ClassUtils.getClassNameForClass(connMgrRuntime
072: .getClass())
073: + ",Name=ConnectionManagerRuntime";
074: mgmtServer.registerMBean(connMgrRuntime, mbeanName);
075: }
076: }
077:
078: /**
079: * Pool of managed connections for an ObjectManager.
080: * Each ObjectManager has its own pool of ManagedConnection's
081: */
082: class ManagedConnectionPool {
083: /**
084: * Connection pool keeps a reference to a connection for each ObjectManager (and so the connection
085: * used by each transaction).
086: * This permits reuse of connections in the same transaction, but not at same time!!!
087: * ManagedConnection must be released to return to pool.
088: * On transaction commit/rollback, connection pool is cleared
089: *
090: * For each combination of ObjectManager-ConnectionFactory there is 0 or 1 ManagedConnection:
091: * Map<ObjectManager, Map<ConnectionFactory,ManagedConnection>>
092: */
093: Map connectionsPool = new HashMap();
094:
095: /**
096: * Remove from pool
097: * @param factory The factory is the nested key
098: * @param om The om is the key for the ManagedConnection
099: */
100: public void removeManagedConnection(ConnectionFactory factory,
101: ObjectManager om) {
102: synchronized (connectionsPool) {
103: Map connectionsForOM = (Map) connectionsPool.get(om);
104: if (connectionsForOM != null) {
105: if (connectionsForOM.remove(factory) != null
106: && connMgrRuntime != null) {
107: connMgrRuntime.decrementActiveConnections();
108: }
109:
110: if (connectionsForOM.size() == 0) {
111: // No connections remaining for this OM so remove the entry for the ObjectManager
112: connectionsPool.remove(om);
113: }
114: }
115: }
116: }
117:
118: /**
119: * Object a ManagedConnection from pool
120: * @param factory
121: * @param om
122: * @return
123: */
124: public ManagedConnection getManagedConnection(
125: ConnectionFactory factory, ObjectManager om) {
126: synchronized (connectionsPool) {
127: Map connectionsForOM = (Map) connectionsPool.get(om);
128: if (connectionsForOM == null) {
129: return null;
130: }
131: //obtain a ManagedConnection for an specific ConnectionFactory
132: ManagedConnection mconn = (ManagedConnection) connectionsForOM
133: .get(factory);
134: if (mconn != null) {
135: if (mconn.isLocked()) {
136: // Enlisted connection that is locked so throw exception
137: throw new JPOXUserException(LOCALISER
138: .msg("009000"));
139: }
140: // Already registered enlisted connection present so return it
141: return mconn;
142: }
143: }
144: return null;
145: }
146:
147: public void putManagedConnection(ConnectionFactory factory,
148: ObjectManager om, ManagedConnection mconn) {
149: synchronized (connectionsPool) {
150: Map connectionsForOM = (Map) connectionsPool.get(om);
151: if (connectionsForOM == null) {
152: connectionsForOM = new HashMap();
153: connectionsPool.put(om, connectionsForOM);
154: }
155: if (connectionsForOM.put(factory, mconn) == null
156: && connMgrRuntime != null) {
157: connMgrRuntime.incrementActiveConnections();
158: }
159: }
160: }
161: }
162:
163: /**
164: * Method to return a connection for this ObjectManager.
165: * If a connection for the ObjectManager exists in the cache will return it.
166: * If no connection exists will create a new one using the ConnectionFactory.
167: * @param factory ConnectionFactory it relates to
168: * @param om The ObjectManager
169: * @param options Options for the connection (e.g isolation). These will override those of the txn itself
170: * @return The ManagedConnection
171: */
172: public ManagedConnection allocateConnection(
173: final ConnectionFactory factory, final ObjectManager om,
174: Map options) {
175: if (om != null) {
176: ManagedConnection mconnFromPool = connectionPool
177: .getManagedConnection(factory, om);
178: if (mconnFromPool != null) {
179: if (JPOXLogger.CONNECTION.isDebugEnabled()) {
180: JPOXLogger.CONNECTION
181: .debug("Connection found in the pool : "
182: + mconnFromPool);
183: }
184: // Already registered enlisted connection present so return it
185: return mconnFromPool;
186: }
187: }
188:
189: // Create new connection
190: Map txOptions = options;
191: if (options == null && om != null) {
192: txOptions = om.getTransaction().getOptions();
193: }
194: ManagedConnection mconn = factory.createManagedConnection(om,
195: txOptions);
196: configureManagedConnectionListener(om, mconn, factory);
197:
198: // Enlist the connection in this transaction
199: if (om != null) {
200: if (om.getTransaction().isActive()) {
201: configureTransactionEventListener(om, mconn);
202: Transaction tx = omfContext.getTransactionManager()
203: .getTransaction(om);
204: //must be set before getting the XAResource
205: mconn.setManagedResource();
206: enlistResource(mconn, tx, options);
207: }
208: // Register this connection against the ObjectManager - connection is valid
209: if (JPOXLogger.CONNECTION.isDebugEnabled()) {
210: JPOXLogger.CONNECTION
211: .debug("Connection added to the pool : "
212: + mconn);
213: }
214: connectionPool.putManagedConnection(factory, om, mconn);
215: }
216:
217: return mconn;
218: }
219:
220: private void configureManagedConnectionListener(
221: final ObjectManager om, final ManagedConnection mconn,
222: final ConnectionFactory factory) {
223: mconn.addListener(new ManagedConnectionResourceListener() {
224: public void managedConnectionPostClose() {
225: if (om != null) {
226: if (JPOXLogger.CONNECTION.isDebugEnabled()) {
227: JPOXLogger.CONNECTION
228: .debug("Connection removed from the pool : "
229: + mconn);
230: }
231: connectionPool.removeManagedConnection(factory, om); // Connection closed so remove
232: }
233: }
234:
235: public void managedConnectionPreClose() {
236: }
237:
238: public void managedConnectionFlushed() {
239: }
240:
241: public void resourcePostClose() {
242: }
243: });
244: }
245:
246: /**
247: * Configure a TransactionEventListener that closes the managed connection when a
248: * transaction commits or rolls back
249: * @param om
250: * @param mconn
251: */
252: private void configureTransactionEventListener(
253: final ObjectManager om, final ManagedConnection mconn) {
254: // Add handler for any enlisted connection to the transaction so we know when to close it
255: om.getTransaction().addTransactionEventListener(
256: new TransactionEventListener() {
257: public void transactionStarted() {
258: }
259:
260: public void transactionRolledBack() {
261: try {
262: mconn.close();
263: } finally {
264: om.getTransaction()
265: .removeTransactionEventListener(
266: this );
267: }
268: }
269:
270: public void transactionCommitted() {
271: try {
272: mconn.close();
273: } finally {
274: om.getTransaction()
275: .removeTransactionEventListener(
276: this );
277: }
278: }
279:
280: public void transactionEnded() {
281: try {
282: mconn.close();
283: } finally {
284: om.getTransaction()
285: .removeTransactionEventListener(
286: this );
287: }
288: }
289:
290: public void transactionPreCommit() {
291: if (mconn.isLocked()) {
292: // Enlisted connection that is locked so throw exception
293: throw new JPOXUserException(LOCALISER
294: .msg("009000"));
295: }
296: }
297:
298: public void transactionPreRollBack() {
299: if (mconn.isLocked()) {
300: // Enlisted connection that is locked so throw exception
301: throw new JPOXUserException(LOCALISER
302: .msg("009000"));
303: }
304: }
305:
306: public void transactionFlushed() {
307: mconn.flush();
308: }
309: });
310: }
311:
312: /**
313: * Enlist the mconn in the transaction if using JPOX's transaction manager
314: * @param mconn
315: * @param tx
316: * @param options
317: */
318: private void enlistResource(ManagedConnection mconn,
319: Transaction tx, Map options) {
320: XAResource res = mconn.getXAResource();
321: if (res != null) {
322: // Enlist the connection resource if has enlistable resource
323: boolean enlistInLocalTM = true;
324: if (options != null
325: && options.get("resource-type") != null
326: && ResourceType.JTA.toString().equalsIgnoreCase(
327: (String) options.get("resource-type"))) {
328: //XAResource will be enlisted in an external JTA container,
329: //so we dont enlist it in the internal Transaction Manager
330: enlistInLocalTM = false;
331: }
332: if (enlistInLocalTM) {
333: tx.enlistResource(res);
334: }
335: }
336: }
337: }
|