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