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