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 cleanup(); 144 session.close(); 145 } 146 147 /** 148 * Called by the application server to change the association of an application-level {@link Connection} handle with 149 * a {@link ManagedConnection} instance. 150 */ 151 @Override 152 public void associateConnection(Object object) throws ResourceException { 153 ConnectionImpl connection = (ConnectionImpl) object; 154 log.debug("associateConnection: " + this + ", connection: " + connection); 155 ManagedConnectionImpl other = connection.getManagedConnection(); 156 if (other != this) { 157 log.debug("associateConnection other: " + other); 158 other.removeConnection(connection); 159 addConnection(connection); 160 } 161 } 162 163 @Override 164 public XAResource getXAResource() { 165 return xaresource; 166 } 167 168 @Override 169 public LocalTransaction getLocalTransaction() { 170 throw new UnsupportedOperationException("Local transactions not supported"); 171 } 172 173 /** 174 * Called by the application server to add a listener who should be notified of all relevant events on this 175 * connection. 176 */ 177 @Override 178 public void addConnectionEventListener(ConnectionEventListener listener) { 179 listeners.add(listener); 180 } 181 182 /** 183 * Called by the application server to remove a listener. 184 */ 185 @Override 186 public void removeConnectionEventListener(ConnectionEventListener listener) { 187 listeners.remove(listener); 188 } 189 190 @Override 191 public ManagedConnectionMetaData getMetaData() { 192 return this; 193 } 194 195 @Override 196 public void setLogWriter(PrintWriter out) { 197 this.out = out; 198 } 199 200 @Override 201 public PrintWriter getLogWriter() { 202 return out; 203 } 204 205 /* 206 * ----- javax.resource.spi.ManagedConnectionMetaData ----- 207 */ 208 209 @Override 210 public String getEISProductName() { 211 return "Nuxeo Core SQL Storage"; 212 } 213 214 @Override 215 public String getEISProductVersion() { 216 return "1.0.0"; 217 } 218 219 @Override 220 public int getMaxConnections() { 221 return Integer.MAX_VALUE; // or lower? 222 } 223 224 @Override 225 public String getUserName() throws ResourceException { 226 return null; 227 } 228 229 /* 230 * ----- Internal ----- 231 */ 232 233 /** 234 * Adds a connection to those using this managed connection. 235 */ 236 private void addConnection(ConnectionImpl connection) { 237 log.debug("addConnection: " + connection); 238 if (connections.add(connection) == false) { 239 throw new IllegalStateException("already known connection " + connection + " in " + this); 240 } 241 connection.setManagedConnection(this); 242 connection.associate(session); 243 } 244 245 /** 246 * Removes a connection to those using this managed connection. 247 */ 248 private void removeConnection(ConnectionImpl connection) { 249 log.debug("removeConnection: " + connection); 250 if (connections.remove(connection) == false) { 251 throw new IllegalStateException("unknown connection " + connection + " in " + this); 252 } 253 connection.setManagedConnection(null); 254 connection.disassociate(); 255 } 256 257 /** 258 * Called by {@link ConnectionImpl#close} when the connection is closed. 259 */ 260 protected void close(ConnectionImpl connection) { 261 removeConnection(connection); 262 sendClosedEvent(connection); 263 } 264 265 /** 266 * Called by {@link ManagedConnectionFactoryImpl#matchManagedConnections}. 267 */ 268 protected ManagedConnectionFactoryImpl getManagedConnectionFactory() { 269 return managedConnectionFactory; 270 } 271 272 /* 273 * ----- Event management ----- 274 */ 275 276 private void sendClosedEvent(ConnectionImpl connection) { 277 log.debug("closing a connection " + connection); 278 sendEvent(ConnectionEvent.CONNECTION_CLOSED, connection, null); 279 } 280 281 protected void sendTxStartedEvent(ConnectionImpl connection) { 282 sendEvent(ConnectionEvent.LOCAL_TRANSACTION_STARTED, connection, null); 283 } 284 285 protected void sendTxCommittedEvent(ConnectionImpl connection) { 286 sendEvent(ConnectionEvent.LOCAL_TRANSACTION_COMMITTED, connection, null); 287 } 288 289 protected void sendTxRolledbackEvent(ConnectionImpl connection) { 290 sendEvent(ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK, connection, null); 291 } 292 293 protected void sendErrorEvent(ConnectionImpl connection, Exception cause) { 294 sendEvent(ConnectionEvent.CONNECTION_ERROR_OCCURRED, connection, cause); 295 } 296 297 private void sendEvent(int type, ConnectionImpl connection, Exception cause) { 298 ConnectionEvent event = new ConnectionEvent(this, type, cause); 299 if (connection != null) { 300 event.setConnectionHandle(connection); 301 } 302 sendEvent(event); 303 } 304 305 /** 306 * Notifies the application server, through the {@link ConnectionEventListener}s it has registered with us, of what 307 * happens with this connection. 308 */ 309 private void sendEvent(ConnectionEvent event) { 310 for (Object object : listeners.getListeners()) { 311 ConnectionEventListener listener = (ConnectionEventListener) object; 312 switch (event.getId()) { 313 case ConnectionEvent.CONNECTION_CLOSED: 314 listener.connectionClosed(event); 315 break; 316 case ConnectionEvent.LOCAL_TRANSACTION_STARTED: 317 listener.localTransactionStarted(event); 318 break; 319 case ConnectionEvent.LOCAL_TRANSACTION_COMMITTED: 320 listener.localTransactionCommitted(event); 321 break; 322 case ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK: 323 listener.localTransactionRolledback(event); 324 break; 325 case ConnectionEvent.CONNECTION_ERROR_OCCURRED: 326 listener.connectionErrorOccurred(event); 327 break; 328 } 329 } 330 } 331 332}