001/* 002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Florent Guillaume 011 */ 012 013package org.nuxeo.ecm.core.storage.sql.ra; 014 015import java.io.PrintWriter; 016import java.util.HashSet; 017import java.util.Set; 018 019import javax.resource.ResourceException; 020import javax.resource.cci.Connection; 021import javax.resource.cci.ConnectionFactory; 022import javax.resource.spi.ConnectionEvent; 023import javax.resource.spi.ConnectionEventListener; 024import javax.resource.spi.ConnectionRequestInfo; 025import javax.resource.spi.LocalTransaction; 026import javax.resource.spi.ManagedConnection; 027import javax.resource.spi.ManagedConnectionFactory; 028import javax.resource.spi.ManagedConnectionMetaData; 029import javax.security.auth.Subject; 030import javax.transaction.xa.XAResource; 031 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.nuxeo.common.collections.ListenerList; 035import org.nuxeo.ecm.core.storage.sql.SessionImpl; 036 037/** 038 * The managed connection represents an actual physical connection to the underlying storage. It is created by the 039 * {@link ManagedConnectionFactory}, and then encapsulated into a {@link Connection} which is then returned to the 040 * application (via the {@link ConnectionFactory}). 041 * <p> 042 * If sharing is allowed, several different {@link Connection}s may be associated to a given {@link ManagedConnection}, 043 * although not at the same time. 044 * 045 * @author Florent Guillaume 046 */ 047public class ManagedConnectionImpl implements ManagedConnection, ManagedConnectionMetaData { 048 049 private static final Log log = LogFactory.getLog(ManagedConnectionImpl.class); 050 051 private PrintWriter out; 052 053 private final ManagedConnectionFactoryImpl managedConnectionFactory; 054 055 /** 056 * All the {@link Connection} handles for this managed connection. There is usually only one, unless sharing is in 057 * effect. 058 */ 059 private final Set<ConnectionImpl> connections; 060 061 /** 062 * The low-level session managed by this connection. 063 */ 064 private final SessionImpl session; 065 066 /** 067 * The wrapped session as a connection-aware xaresource. 068 */ 069 private final XAResource xaresource; 070 071 /** 072 * List of listeners set by the application server which we must notify of all activity happening on our 073 * {@link Connection}. 074 */ 075 private final ListenerList listeners; 076 077 /** 078 * Creates a new physical connection to the underlying storage. Called by the {@link ManagedConnectionFactory} when 079 * it needs a new connection. 080 * 081 * @throws ResourceException 082 */ 083 public ManagedConnectionImpl(ManagedConnectionFactoryImpl managedConnectionFactory) throws ResourceException { 084 log.debug("construct: " + this); 085 if (log.isTraceEnabled()) { 086 log.trace("debug stack trace", new Exception()); 087 } 088 out = managedConnectionFactory.getLogWriter(); 089 this.managedConnectionFactory = managedConnectionFactory; 090 connections = new HashSet<ConnectionImpl>(); 091 listeners = new ListenerList(); 092 // create the underlying session 093 session = managedConnectionFactory.getConnection(); 094 xaresource = session.getXAResource(); 095 } 096 097 /* 098 * ----- javax.resource.spi.ManagedConnection ----- 099 */ 100 101 /** 102 * Creates a new {@link Connection} handle to this {@link ManagedConnection} . 103 */ 104 @Override 105 public synchronized Connection getConnection(Subject subject, ConnectionRequestInfo connectionRequestInfo) 106 throws ResourceException { 107 // connectionRequestInfo unused 108 log.debug("getConnection: " + this); 109 ConnectionImpl connection = new ConnectionImpl(this); 110 addConnection(connection); 111 return connection; 112 } 113 114 /** 115 * Cleans up the physical connection, so that it may be reused. 116 * <p> 117 * Called by the application server before putting back this {@link ManagedConnection} into the application server 118 * pool. 119 * <p> 120 * Later, the application server may call {@link #getConnection} again. 121 */ 122 @Override 123 public void cleanup() { 124 log.debug("cleanup: " + this); 125 if (log.isTraceEnabled()) { 126 log.trace("debug stack trace", new Exception()); 127 } 128 synchronized (connections) { 129 // TODO session.cancel 130 connections.clear(); 131 } 132 } 133 134 /** 135 * Destroys the physical connection. 136 * <p> 137 * Called by the application server before this {@link ManagedConnection} is destroyed. 138 */ 139 @Override 140 public void destroy() throws ResourceException { 141 log.debug("destroy: " + this); 142 cleanup(); 143 session.close(); 144 } 145 146 /** 147 * Called by the application server to change the association of an application-level {@link Connection} handle with 148 * a {@link ManagedConnection} instance. 149 */ 150 @Override 151 public void associateConnection(Object object) throws ResourceException { 152 ConnectionImpl connection = (ConnectionImpl) object; 153 log.debug("associateConnection: " + this + ", connection: " + connection); 154 ManagedConnectionImpl other = connection.getManagedConnection(); 155 if (other != this) { 156 log.debug("associateConnection other: " + other); 157 other.removeConnection(connection); 158 addConnection(connection); 159 } 160 } 161 162 @Override 163 public XAResource getXAResource() { 164 return xaresource; 165 } 166 167 @Override 168 public LocalTransaction getLocalTransaction() { 169 throw new UnsupportedOperationException("Local transactions not supported"); 170 } 171 172 /** 173 * Called by the application server to add a listener who should be notified of all relevant events on this 174 * connection. 175 */ 176 @Override 177 public void addConnectionEventListener(ConnectionEventListener listener) { 178 listeners.add(listener); 179 } 180 181 /** 182 * Called by the application server to remove a listener. 183 */ 184 @Override 185 public void removeConnectionEventListener(ConnectionEventListener listener) { 186 listeners.remove(listener); 187 } 188 189 @Override 190 public ManagedConnectionMetaData getMetaData() { 191 return this; 192 } 193 194 @Override 195 public void setLogWriter(PrintWriter out) { 196 this.out = out; 197 } 198 199 @Override 200 public PrintWriter getLogWriter() { 201 return out; 202 } 203 204 /* 205 * ----- javax.resource.spi.ManagedConnectionMetaData ----- 206 */ 207 208 @Override 209 public String getEISProductName() { 210 return "Nuxeo Core SQL Storage"; 211 } 212 213 @Override 214 public String getEISProductVersion() { 215 return "1.0.0"; 216 } 217 218 @Override 219 public int getMaxConnections() { 220 return Integer.MAX_VALUE; // or lower? 221 } 222 223 @Override 224 public String getUserName() throws ResourceException { 225 return null; 226 } 227 228 /* 229 * ----- Internal ----- 230 */ 231 232 /** 233 * Adds a connection to those using this managed connection. 234 */ 235 private void addConnection(ConnectionImpl connection) { 236 log.debug("addConnection: " + connection); 237 if (connections.add(connection) == false) { 238 throw new IllegalStateException("already known connection " + connection + " in " + this); 239 } 240 connection.setManagedConnection(this); 241 connection.associate(session); 242 } 243 244 /** 245 * Removes a connection to those using this managed connection. 246 */ 247 private void removeConnection(ConnectionImpl connection) { 248 log.debug("removeConnection: " + connection); 249 if (connections.remove(connection) == false) { 250 throw new IllegalStateException("unknown connection " + connection + " in " + this); 251 } 252 connection.setManagedConnection(null); 253 connection.disassociate(); 254 } 255 256 /** 257 * Called by {@link ConnectionImpl#close} when the connection is closed. 258 */ 259 protected void close(ConnectionImpl connection) { 260 removeConnection(connection); 261 sendClosedEvent(connection); 262 } 263 264 /** 265 * Called by {@link ManagedConnectionFactoryImpl#matchManagedConnections}. 266 */ 267 protected ManagedConnectionFactoryImpl getManagedConnectionFactory() { 268 return managedConnectionFactory; 269 } 270 271 /* 272 * ----- Event management ----- 273 */ 274 275 private void sendClosedEvent(ConnectionImpl connection) { 276 log.debug("closing a connection " + connection); 277 sendEvent(ConnectionEvent.CONNECTION_CLOSED, connection, null); 278 } 279 280 protected void sendTxStartedEvent(ConnectionImpl connection) { 281 sendEvent(ConnectionEvent.LOCAL_TRANSACTION_STARTED, connection, null); 282 } 283 284 protected void sendTxCommittedEvent(ConnectionImpl connection) { 285 sendEvent(ConnectionEvent.LOCAL_TRANSACTION_COMMITTED, connection, null); 286 } 287 288 protected void sendTxRolledbackEvent(ConnectionImpl connection) { 289 sendEvent(ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK, connection, null); 290 } 291 292 protected void sendErrorEvent(ConnectionImpl connection, Exception cause) { 293 sendEvent(ConnectionEvent.CONNECTION_ERROR_OCCURRED, connection, cause); 294 } 295 296 private void sendEvent(int type, ConnectionImpl connection, Exception cause) { 297 ConnectionEvent event = new ConnectionEvent(this, type, cause); 298 if (connection != null) { 299 event.setConnectionHandle(connection); 300 } 301 sendEvent(event); 302 } 303 304 /** 305 * Notifies the application server, through the {@link ConnectionEventListener}s it has registered with us, of what 306 * happens with this connection. 307 */ 308 private void sendEvent(ConnectionEvent event) { 309 for (Object object : listeners.getListeners()) { 310 ConnectionEventListener listener = (ConnectionEventListener) object; 311 switch (event.getId()) { 312 case ConnectionEvent.CONNECTION_CLOSED: 313 listener.connectionClosed(event); 314 break; 315 case ConnectionEvent.LOCAL_TRANSACTION_STARTED: 316 listener.localTransactionStarted(event); 317 break; 318 case ConnectionEvent.LOCAL_TRANSACTION_COMMITTED: 319 listener.localTransactionCommitted(event); 320 break; 321 case ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK: 322 listener.localTransactionRolledback(event); 323 break; 324 case ConnectionEvent.CONNECTION_ERROR_OCCURRED: 325 listener.connectionErrorOccurred(event); 326 break; 327 } 328 } 329 } 330 331}