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.Serializable; 016import java.util.HashSet; 017import java.util.List; 018import java.util.Map; 019import java.util.Set; 020 021import javax.resource.ResourceException; 022import javax.resource.cci.ConnectionFactory; 023import javax.resource.cci.ConnectionMetaData; 024import javax.resource.cci.Interaction; 025import javax.resource.cci.LocalTransaction; 026import javax.resource.cci.ResultSetInfo; 027 028import org.apache.commons.logging.LogFactory; 029import org.nuxeo.ecm.core.api.IterableQueryResult; 030import org.nuxeo.ecm.core.api.NuxeoException; 031import org.nuxeo.ecm.core.api.PartialList; 032import org.nuxeo.ecm.core.model.LockManager; 033import org.nuxeo.ecm.core.query.QueryFilter; 034import org.nuxeo.ecm.core.storage.sql.Mapper; 035import org.nuxeo.ecm.core.storage.sql.Model; 036import org.nuxeo.ecm.core.storage.sql.Node; 037import org.nuxeo.ecm.core.storage.sql.Session; 038import org.nuxeo.ecm.core.storage.sql.SessionImpl; 039 040/** 041 * A connection is a handle to the underlying storage. It is returned by the {@link ConnectionFactory} to application 042 * code. 043 * <p> 044 * The actual link to the underlying storage ({@link Session}) is provided by the 045 * {@link javax.resource.spi.ManagedConnection} which created this {@link javax.resource.cci.Connection}. 046 * 047 * @author Florent Guillaume 048 */ 049public class ConnectionImpl implements Session { 050 051 private ManagedConnectionImpl managedConnection; 052 053 private SessionImpl session; 054 055 public ConnectionImpl(ManagedConnectionImpl managedConnection) { 056 this.managedConnection = managedConnection; 057 } 058 059 /* 060 * ----- callbacks ----- 061 */ 062 063 /** 064 * Called by {@link ManagedConnectionImpl#associateConnection}. 065 */ 066 protected ManagedConnectionImpl getManagedConnection() { 067 return managedConnection; 068 } 069 070 /** 071 * Called by {@link ManagedConnectionImpl#associateConnection}. 072 */ 073 protected void setManagedConnection(ManagedConnectionImpl managedConnection) { 074 this.managedConnection = managedConnection; 075 } 076 077 /** 078 * Called by {@link ManagedConnectionImpl#addConnection}. 079 */ 080 protected void associate(SessionImpl session) { 081 this.session = session; 082 } 083 084 /** 085 * Called by {@link ManagedConnectionImpl#removeConnection}. 086 */ 087 protected void disassociate() { 088 closeStillOpenQueryResults(); 089 session = null; 090 } 091 092 /* 093 * ----- javax.resource.cci.Connection ----- 094 */ 095 096 protected Throwable closeTrace; 097 098 @Override 099 public void close() throws ResourceException { 100 if (managedConnection == null) { 101 IllegalStateException error = new IllegalStateException("connection already closed " + this); 102 error.addSuppressed(closeTrace); 103 throw error; 104 } 105 try { 106 managedConnection.close(this); 107 } finally { 108 closeTrace = new Throwable("close stack trace"); 109 managedConnection = null; 110 } 111 } 112 113 @Override 114 public Interaction createInteraction() throws ResourceException { 115 throw new UnsupportedOperationException(); 116 } 117 118 @Override 119 public LocalTransaction getLocalTransaction() throws ResourceException { 120 throw new UnsupportedOperationException(); 121 } 122 123 @Override 124 public ConnectionMetaData getMetaData() throws ResourceException { 125 throw new UnsupportedOperationException(); 126 } 127 128 @Override 129 public ResultSetInfo getResultSetInfo() throws ResourceException { 130 throw new UnsupportedOperationException(); 131 } 132 133 /* 134 * ----- org.nuxeo.ecm.core.storage.sql.Session ----- 135 */ 136 137 private Session getSession() { 138 if (session == null) { 139 throw new NuxeoException("Cannot use closed connection handle: " + this); 140 } 141 return session; 142 } 143 144 @Override 145 public Mapper getMapper() { 146 return getSession().getMapper(); 147 } 148 149 @Override 150 public boolean isLive() { 151 return session != null && session.isLive(); 152 } 153 154 @Override 155 public boolean isStateSharedByAllThreadSessions() { 156 // the JCA semantics is that in the same thread all handles point to the 157 // same underlying session 158 return true; 159 } 160 161 @Override 162 public String getRepositoryName() { 163 return getSession().getRepositoryName(); 164 } 165 166 @Override 167 public Model getModel() { 168 return getSession().getModel(); 169 } 170 171 @Override 172 public void save() { 173 getSession().save(); 174 } 175 176 @Override 177 public Node getRootNode() { 178 return getSession().getRootNode(); 179 } 180 181 @Override 182 public Node getNodeById(Serializable id) { 183 return getSession().getNodeById(id); 184 } 185 186 @Override 187 public List<Node> getNodesByIds(List<Serializable> ids) { 188 return getSession().getNodesByIds(ids); 189 } 190 191 @Override 192 public Node getNodeByPath(String path, Node node) { 193 return getSession().getNodeByPath(path, node); 194 } 195 196 @Override 197 public boolean addMixinType(Node node, String mixin) { 198 return getSession().addMixinType(node, mixin); 199 } 200 201 @Override 202 public boolean removeMixinType(Node node, String mixin) { 203 return getSession().removeMixinType(node, mixin); 204 } 205 206 @Override 207 public boolean hasChildNode(Node parent, String name, boolean complexProp) { 208 return getSession().hasChildNode(parent, name, complexProp); 209 } 210 211 @Override 212 public Node getChildNode(Node parent, String name, boolean complexProp) { 213 return getSession().getChildNode(parent, name, complexProp); 214 } 215 216 @Override 217 public boolean hasChildren(Node parent, boolean complexProp) { 218 return getSession().hasChildren(parent, complexProp); 219 } 220 221 @Override 222 public List<Node> getChildren(Node parent, String name, boolean complexProp) { 223 return getSession().getChildren(parent, name, complexProp); 224 } 225 226 @Override 227 public Node addChildNode(Node parent, String name, Long pos, String typeName, boolean complexProp) { 228 return getSession().addChildNode(parent, name, pos, typeName, complexProp); 229 } 230 231 @Override 232 public Node addChildNode(Serializable id, Node parent, String name, Long pos, String typeName, 233 boolean complexProp) { 234 return getSession().addChildNode(id, parent, name, pos, typeName, complexProp); 235 } 236 237 @Override 238 public void removeNode(Node node) { 239 getSession().removeNode(node); 240 } 241 242 @Override 243 public void removePropertyNode(Node node) { 244 getSession().removePropertyNode(node); 245 } 246 247 @Override 248 public Node getParentNode(Node node) { 249 return getSession().getParentNode(node); 250 } 251 252 @Override 253 public String getPath(Node node) { 254 return getSession().getPath(node); 255 } 256 257 @Override 258 public void orderBefore(Node node, Node src, Node dest) { 259 getSession().orderBefore(node, src, dest); 260 } 261 262 @Override 263 public Node move(Node source, Node parent, String name) { 264 return getSession().move(source, parent, name); 265 } 266 267 @Override 268 public Node copy(Node source, Node parent, String name) { 269 return getSession().copy(source, parent, name); 270 } 271 272 @Override 273 public Node checkIn(Node node, String label, String checkinComment) { 274 return getSession().checkIn(node, label, checkinComment); 275 } 276 277 @Override 278 public void checkOut(Node node) { 279 getSession().checkOut(node); 280 } 281 282 @Override 283 public void restore(Node node, Node version) { 284 getSession().restore(node, version); 285 } 286 287 @Override 288 public Node getVersionByLabel(Serializable versionSeriesId, String label) { 289 return getSession().getVersionByLabel(versionSeriesId, label); 290 } 291 292 @Override 293 public List<Node> getVersions(Serializable versionSeriesId) { 294 return getSession().getVersions(versionSeriesId); 295 } 296 297 @Override 298 public Node getLastVersion(Serializable versionSeriesId) { 299 return getSession().getLastVersion(versionSeriesId); 300 } 301 302 @Override 303 public List<Node> getProxies(Node document, Node parent) { 304 return getSession().getProxies(document, parent); 305 } 306 307 @Override 308 public void setProxyTarget(Node proxy, Serializable targetId) { 309 getSession().setProxyTarget(proxy, targetId); 310 } 311 312 @Override 313 public Node addProxy(Serializable targetId, Serializable versionSeriesId, Node parent, String name, Long pos) { 314 return getSession().addProxy(targetId, versionSeriesId, parent, name, pos); 315 } 316 317 @Override 318 public PartialList<Serializable> query(String query, QueryFilter queryFilter, boolean countTotal) { 319 return getSession().query(query, queryFilter, countTotal); 320 } 321 322 @Override 323 public PartialList<Serializable> query(String query, String queryType, QueryFilter queryFilter, long countUpTo) { 324 return getSession().query(query, queryType, queryFilter, countUpTo); 325 } 326 327 @Override 328 public IterableQueryResult queryAndFetch(String query, String queryType, QueryFilter queryFilter, 329 Object... params) { 330 IterableQueryResult result = getSession().queryAndFetch(query, queryType, queryFilter, params); 331 noteQueryResult(result); 332 return result; 333 } 334 335 public static class QueryResultContextException extends Exception { 336 private static final long serialVersionUID = 1L; 337 338 public final IterableQueryResult queryResult; 339 340 public QueryResultContextException(IterableQueryResult queryResult) { 341 super("queryAndFetch call context"); 342 this.queryResult = queryResult; 343 } 344 } 345 346 protected final Set<QueryResultContextException> queryResults = new HashSet<QueryResultContextException>(); 347 348 protected void noteQueryResult(IterableQueryResult result) { 349 queryResults.add(new QueryResultContextException(result)); 350 } 351 352 protected void closeStillOpenQueryResults() { 353 for (QueryResultContextException context : queryResults) { 354 if (!context.queryResult.isLife()) { 355 continue; 356 } 357 try { 358 context.queryResult.close(); 359 } catch (RuntimeException e) { 360 LogFactory.getLog(ConnectionImpl.class).error("Cannot close query result", e); 361 } finally { 362 LogFactory.getLog(ConnectionImpl.class).warn( 363 "Closing a query results for you, check stack trace for allocating point", context); 364 } 365 } 366 queryResults.clear(); 367 } 368 369 @Override 370 public LockManager getLockManager() { 371 return getSession().getLockManager(); 372 } 373 374 @Override 375 public void requireReadAclsUpdate() { 376 if (session != null) { 377 session.requireReadAclsUpdate(); 378 } 379 } 380 381 @Override 382 public void updateReadAcls() { 383 getSession().updateReadAcls(); 384 } 385 386 @Override 387 public void rebuildReadAcls() { 388 getSession().rebuildReadAcls(); 389 } 390 391 @Override 392 public Map<String, String> getBinaryFulltext(Serializable id) { 393 return getSession().getBinaryFulltext(id); 394 } 395 396}