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