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.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026 027import javax.resource.ResourceException; 028import javax.resource.cci.ConnectionFactory; 029import javax.resource.cci.ConnectionMetaData; 030import javax.resource.cci.Interaction; 031import javax.resource.cci.LocalTransaction; 032import javax.resource.cci.ResultSetInfo; 033 034import org.apache.commons.logging.LogFactory; 035import org.nuxeo.ecm.core.api.IterableQueryResult; 036import org.nuxeo.ecm.core.api.NuxeoException; 037import org.nuxeo.ecm.core.api.PartialList; 038import org.nuxeo.ecm.core.api.ScrollResult; 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 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 ScrollResult scroll(String query, int batchSize, int keepAliveSeconds) { 208 return getSession().scroll(query, batchSize, keepAliveSeconds); 209 } 210 211 @Override 212 public ScrollResult scroll(String scrollId) { 213 return getSession().scroll(scrollId); 214 } 215 216 @Override 217 public boolean hasChildNode(Node parent, String name, boolean complexProp) { 218 return getSession().hasChildNode(parent, name, complexProp); 219 } 220 221 @Override 222 public Node getChildNode(Node parent, String name, boolean complexProp) { 223 return getSession().getChildNode(parent, name, complexProp); 224 } 225 226 @Override 227 public boolean hasChildren(Node parent, boolean complexProp) { 228 return getSession().hasChildren(parent, complexProp); 229 } 230 231 @Override 232 public List<Node> getChildren(Node parent, String name, boolean complexProp) { 233 return getSession().getChildren(parent, name, complexProp); 234 } 235 236 @Override 237 public Node addChildNode(Node parent, String name, Long pos, String typeName, boolean complexProp) { 238 return getSession().addChildNode(parent, name, pos, typeName, complexProp); 239 } 240 241 @Override 242 public Node addChildNode(Serializable id, Node parent, String name, Long pos, String typeName, 243 boolean complexProp) { 244 return getSession().addChildNode(id, parent, name, pos, typeName, complexProp); 245 } 246 247 @Override 248 public void removeNode(Node node) { 249 getSession().removeNode(node); 250 } 251 252 @Override 253 public void removePropertyNode(Node node) { 254 getSession().removePropertyNode(node); 255 } 256 257 @Override 258 public Node getParentNode(Node node) { 259 return getSession().getParentNode(node); 260 } 261 262 @Override 263 public String getPath(Node node) { 264 return getSession().getPath(node); 265 } 266 267 @Override 268 public void orderBefore(Node node, Node src, Node dest) { 269 getSession().orderBefore(node, src, dest); 270 } 271 272 @Override 273 public Node move(Node source, Node parent, String name) { 274 return getSession().move(source, parent, name); 275 } 276 277 @Override 278 public Node copy(Node source, Node parent, String name) { 279 return getSession().copy(source, parent, name); 280 } 281 282 @Override 283 public Node checkIn(Node node, String label, String checkinComment) { 284 return getSession().checkIn(node, label, checkinComment); 285 } 286 287 @Override 288 public void checkOut(Node node) { 289 getSession().checkOut(node); 290 } 291 292 @Override 293 public void restore(Node node, Node version) { 294 getSession().restore(node, version); 295 } 296 297 @Override 298 public Node getVersionByLabel(Serializable versionSeriesId, String label) { 299 return getSession().getVersionByLabel(versionSeriesId, label); 300 } 301 302 @Override 303 public List<Node> getVersions(Serializable versionSeriesId) { 304 return getSession().getVersions(versionSeriesId); 305 } 306 307 @Override 308 public Node getLastVersion(Serializable versionSeriesId) { 309 return getSession().getLastVersion(versionSeriesId); 310 } 311 312 @Override 313 public List<Node> getProxies(Node document, Node parent) { 314 return getSession().getProxies(document, parent); 315 } 316 317 @Override 318 public void setProxyTarget(Node proxy, Serializable targetId) { 319 getSession().setProxyTarget(proxy, targetId); 320 } 321 322 @Override 323 public Node addProxy(Serializable targetId, Serializable versionSeriesId, Node parent, String name, Long pos) { 324 return getSession().addProxy(targetId, versionSeriesId, parent, name, pos); 325 } 326 327 @Override 328 public PartialList<Serializable> query(String query, QueryFilter queryFilter, boolean countTotal) { 329 return getSession().query(query, queryFilter, countTotal); 330 } 331 332 @Override 333 public PartialList<Serializable> query(String query, String queryType, QueryFilter queryFilter, long countUpTo) { 334 return getSession().query(query, queryType, queryFilter, countUpTo); 335 } 336 337 @Override 338 public IterableQueryResult queryAndFetch(String query, String queryType, QueryFilter queryFilter, 339 Object... params) { 340 IterableQueryResult result = getSession().queryAndFetch(query, queryType, queryFilter, params); 341 noteQueryResult(result); 342 return result; 343 } 344 345 @Override 346 public IterableQueryResult queryAndFetch(String query, String queryType, QueryFilter queryFilter, 347 boolean distinctDocuments, Object... params) { 348 IterableQueryResult result = getSession().queryAndFetch(query, queryType, queryFilter, distinctDocuments, 349 params); 350 noteQueryResult(result); 351 return result; 352 } 353 354 @Override 355 public PartialList<Map<String,Serializable>> queryProjection(String query, String queryType, QueryFilter queryFilter, 356 boolean distinctDocuments, long countUpTo, Object... params) { 357 return getSession().queryProjection(query, queryType, queryFilter, distinctDocuments, countUpTo, params); 358 } 359 360 public static class QueryResultContextException extends Exception { 361 private static final long serialVersionUID = 1L; 362 363 public final IterableQueryResult queryResult; 364 365 public QueryResultContextException(IterableQueryResult queryResult) { 366 super("queryAndFetch call context"); 367 this.queryResult = queryResult; 368 } 369 } 370 371 protected final Set<QueryResultContextException> queryResults = new HashSet<>(); 372 373 protected void noteQueryResult(IterableQueryResult result) { 374 queryResults.add(new QueryResultContextException(result)); 375 } 376 377 protected void closeStillOpenQueryResults() { 378 for (QueryResultContextException context : queryResults) { 379 if (!context.queryResult.mustBeClosed()) { 380 continue; 381 } 382 try { 383 context.queryResult.close(); 384 } catch (RuntimeException e) { 385 LogFactory.getLog(ConnectionImpl.class).error("Cannot close query result", e); 386 } finally { 387 LogFactory.getLog(ConnectionImpl.class) 388 .warn("Closing a query results for you, check stack trace for allocating point", context); 389 } 390 } 391 queryResults.clear(); 392 } 393 394 @Override 395 public LockManager getLockManager() { 396 return getSession().getLockManager(); 397 } 398 399 @Override 400 public void requireReadAclsUpdate() { 401 if (session != null) { 402 session.requireReadAclsUpdate(); 403 } 404 } 405 406 @Override 407 public void updateReadAcls() { 408 getSession().updateReadAcls(); 409 } 410 411 @Override 412 public void rebuildReadAcls() { 413 getSession().rebuildReadAcls(); 414 } 415 416 @Override 417 public Map<String, String> getBinaryFulltext(Serializable id) { 418 return getSession().getBinaryFulltext(id); 419 } 420 421 @Override 422 public boolean isChangeTokenEnabled() { 423 return getSession().isChangeTokenEnabled(); 424 } 425 426 @Override 427 public void markUserChange(Serializable id) { 428 getSession().markUserChange(id); 429 } 430 431}