/* * $Id$ * * Copyright 2006, The jCoderZ.org Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the jCoderZ.org Project nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jcoderz.commons.connector.file; import java.io.PrintWriter; import java.util.HashSet; import java.util.Iterator; import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.resource.NotSupportedException; import javax.resource.ResourceException; import javax.resource.spi.ConnectionEvent; import javax.resource.spi.ConnectionEventListener; import javax.resource.spi.ConnectionRequestInfo; import javax.resource.spi.LocalTransaction; import javax.resource.spi.ManagedConnection; import javax.resource.spi.ManagedConnectionFactory; import javax.resource.spi.ManagedConnectionMetaData; import javax.security.auth.Subject; import javax.transaction.xa.XAResource; import org.jcoderz.commons.connector.ConnectionHandle; import org.jcoderz.commons.connector.ConnectionNotificationListener; import org.jcoderz.commons.connector.SecurityUtil; import org.jcoderz.commons.connector.UserPassword; /** * This Managed Connection is the factory for a File System Connction. * * The File System Connector is not transactional, so the methods * {@linkplain #getLocalTransaction()} and {@linkplain #getXAResource()} always * throw the {@link javax.resource.NotSupportedException}. * */ public class FsManagedConnectionImpl implements ManagedConnection, ConnectionNotificationListener { /** The full qualified name of this class. */ private static final String CLASSNAME = FsManagedConnectionImpl.class.getName(); /** The logger to use. */ private static final Logger logger = Logger.getLogger(CLASSNAME); private static final Random RANDOM = new Random(); private final FsManagedConnectionMetaData mMetaData; /** Registered ConnectionEventListeners. */ private final Set mConnectionEventListeners = new HashSet(); /** All active connections, created by this Factory. */ private final Set mConnections = new HashSet(); /** The PrintWriter instance to use. */ private PrintWriter mPrintWriter; private final UserPassword mUp; private final ConnectionRequestInfo mCri; private final ManagedConnectionFactory mMcf; private final int mIndex; private final String mStringified; /** * Constructor. * @param mcf The underlying ManagedConnectionFactory * @param up The UserPassword to be used. * @param cri Connection Request Info */ public FsManagedConnectionImpl (ManagedConnectionFactory mcf, UserPassword up, ConnectionRequestInfo cri) { synchronized (RANDOM) { mIndex = RANDOM.nextInt(Integer.MAX_VALUE); } mStringified = "FsManagedConnectionImpl[" + mIndex + "]"; mMcf = mcf; mUp = UserPassword.fromUserPassword(up); mCri = cri; mMetaData = new FsManagedConnectionMetaData(mUp.getUserName()); logger.fine("Created FsManagedConnectionImpl[" + mIndex + "]"); } /** {@inheritDoc} */ public Object getConnection (Subject subject, ConnectionRequestInfo cri) throws ResourceException { final String method = "getConnection"; final boolean finer = logger.isLoggable(Level.FINER); if (finer) { logger.entering(CLASSNAME, method, new Object [] {subject, cri}); } final UserPassword up = SecurityUtil.getUserPassword(subject, mMcf, cri); if (!mUp.equals(up)) { // JCA 1.0 8.2.7 // If a resource adapter does not support re-authentication, the // getConnection method should throw // javax.resource.spi. SecurityException if the passed Subject in the // getConnection method is different from the security context // associated with the ManagedConnection instance. final javax.resource.spi.SecurityException rse = new javax.resource.spi.SecurityException( "Re-authentication is not supported."); if (finer) { logger.throwing(CLASSNAME, method, rse); } throw rse; } final Properties props; if (cri instanceof FsConnectionRequestInfo) { props = ((FsConnectionRequestInfo) cri).getProperties(); } else { props = null; } final Object result = new FsConnectionImpl(this, props); registerHandle(result, finer); if (finer) { logger.exiting(CLASSNAME, method, result); } return result; } /** {@inheritDoc} */ public void destroy () throws ResourceException { final boolean finer = logger.isLoggable(Level.FINER); if (finer) { logger.entering(CLASSNAME, "destroy"); } cleanupConnection(); if (finer) { logger.exiting(CLASSNAME, "destroy"); } } /** {@inheritDoc} */ public void cleanup () throws ResourceException { final boolean finer = logger.isLoggable(Level.FINER); if (finer) { logger.entering(CLASSNAME, "cleanup"); } cleanupConnection(); if (finer) { logger.exiting(CLASSNAME, "cleanup"); } } /** {@inheritDoc} */ public void associateConnection (Object connection) throws ResourceException { final boolean finer = logger.isLoggable(Level.FINER); if (finer) { logger.entering(CLASSNAME, "associateConnection", connection); } if (!(connection instanceof ConnectionHandle)) { final ResourceException re = new ResourceException("Can not associate " + "the new connection handle: '" + connection + "'."); logger.throwing(CLASSNAME, "associateConnection", re); throw re; } final ConnectionHandle cb = (ConnectionHandle) connection; cb.changeAssociation(this); registerHandle(cb, finer); if (finer) { logger.exiting(CLASSNAME, "associateConnection"); } } /** {@inheritDoc} */ public void addConnectionEventListener (ConnectionEventListener cel) { logger.entering(CLASSNAME, "addConnectionEventListener", cel); synchronized (mConnectionEventListeners) { mConnectionEventListeners.add(cel); } logger.exiting(CLASSNAME, "addConnectionEventListener"); } /** {@inheritDoc} */ public void removeConnectionEventListener (ConnectionEventListener cel) { logger.entering(CLASSNAME, "removeConnectionEventListener", cel); synchronized (mConnectionEventListeners) { mConnectionEventListeners.remove(cel); } logger.exiting(CLASSNAME, "removeConnectionEventListener", cel); } /** * Always throws the {@link NotSupportedException}. * The File System Connector does not support any XA Transaction. * * @return never return. * * @throws ResourceException Always thrown. * * @see javax.resource.spi.ManagedConnection#getXAResource() */ public XAResource getXAResource () throws ResourceException { throw new NotSupportedException("Resource Adapter does not support " + "XA Transaction."); } /** * Always throws the {@link NotSupportedException}. * The File System Connector does not support any Local Transaction. * * @return never return. * * @throws ResourceException Always thrown. * * @see javax.resource.spi.ManagedConnection#getLocalTransaction() */ public LocalTransaction getLocalTransaction () throws ResourceException { throw new NotSupportedException("Resource Adapter does not support " + "Local Transaction."); } /** {@inheritDoc} */ public ManagedConnectionMetaData getMetaData () throws ResourceException { return mMetaData; } /** {@inheritDoc} */ public void setLogWriter (PrintWriter pw) throws ResourceException { logger.entering(CLASSNAME, "setLogWriter", pw); mPrintWriter = pw; logger.exiting(CLASSNAME, "setLogWriter"); } /** {@inheritDoc} */ public PrintWriter getLogWriter () throws ResourceException { if (logger.isLoggable(Level.FINER)) { logger.entering(CLASSNAME, "getLogWriter"); logger.exiting(CLASSNAME, "getLogWriter", mPrintWriter); } return mPrintWriter; } /** {@inheritDoc} */ public void notifyConnectionClosed (Object source) { final boolean finer = logger.isLoggable(Level.FINER); if (finer) { logger.entering(CLASSNAME, "notifyConnectionClosed", source); } // This connection has been closed deregisterHandle(source, finer); // Connection Closed Event. final ConnectionEvent closedEvent = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED); // Set connection handle closedEvent.setConnectionHandle(source); // Notify all connection listeners notifyEvent(closedEvent); if (finer) { logger.exiting(CLASSNAME, "notifyConnectionClosed"); } } /** {@inheritDoc} */ public void notifyConnectionErrorOccurred (Object source, Exception e) { logger.entering(CLASSNAME, "notifyConnectionErrorOccurred", new Object [] {source, e}); // Connection Closed Event. final ConnectionEvent closedEvent = new ConnectionEvent(this, ConnectionEvent.CONNECTION_ERROR_OCCURRED, e); // Set connection handle closedEvent.setConnectionHandle(source); // Notify all connection listeners notifyEvent(closedEvent); logger.exiting(CLASSNAME, "notifyConnectionErrorOccurred"); } /** {@inheritDoc} */ public void notifyConnectionDissociated (Object source) { final boolean finer = logger.isLoggable(Level.FINER); if (finer) { logger.entering(CLASSNAME, "notifyConnectionDissociated", source); } deregisterHandle(source, finer); if (finer) { logger.exiting(CLASSNAME, "notifyConnectionDissociated"); } } /** * @return UserPassword set for this managed connection. */ UserPassword getUserPassword () { return mUp; } /** {@inheritDoc} */ public String toString () { return mStringified; } /** {@inheritDoc} */ public int hashCode () { return mIndex; } /** * Indicates whether some other object is "equal to" this one. * * @param obj the object to compare to. * @return true if this object is the same as the obj argument; false * otherwise. */ public boolean equals (Object obj) { return (obj instanceof FsManagedConnectionImpl && ((FsManagedConnectionImpl) obj).mIndex == this.mIndex); } private void registerHandle (final Object result, boolean loggable) { mConnections.add(result); if (loggable) { logger.finer(toString() + " added " + result + ", new connection count " + mConnections.size()); } } private void deregisterHandle (Object source, boolean loggable) { mConnections.remove(source); if (loggable) { logger.finer(toString() + " removed " + source + ", new connection count " + mConnections.size()); } } /** * Cleans up all handles created by this factory and frees all allocaded * resources. Removes all registered notification listeners. */ private void cleanupConnection () { final boolean finer = logger.isLoggable(Level.FINER); if (finer) { logger.entering(CLASSNAME, "cleanupConnection"); } final Iterator itr = mConnections.iterator(); ConnectionHandle current = null; while (itr.hasNext()) { current = (ConnectionHandle) itr.next(); try { current.cleanUp(); } catch (ResourceException re) { logger.log(Level.WARNING, "Could not clean up the connection '" + current + "'.", re); } } mConnections.clear(); if (finer) { logger.exiting(CLASSNAME, "cleanupConnection"); } } private void notifyEvent (final ConnectionEvent event) { final Object [] a; synchronized (mConnectionEventListeners) { // to avoid locking on the mConnectionEventListeners object a = mConnectionEventListeners.toArray(); } // Notify all listeners for (int i = 0; i < a.length; i++) { ((ConnectionEventListener) a[i]).connectionClosed(event); } } }