/*
* (C) Copyright 2006-2017 Nuxeo (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Florent Guillaume
*/
package org.nuxeo.ecm.core.storage.sql.ra;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.ConnectionMetaData;
import javax.resource.cci.Interaction;
import javax.resource.cci.LocalTransaction;
import javax.resource.cci.ResultSetInfo;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.IterableQueryResult;
import org.nuxeo.ecm.core.api.NuxeoException;
import org.nuxeo.ecm.core.api.PartialList;
import org.nuxeo.ecm.core.api.ScrollResult;
import org.nuxeo.ecm.core.model.LockManager;
import org.nuxeo.ecm.core.query.QueryFilter;
import org.nuxeo.ecm.core.storage.sql.Mapper;
import org.nuxeo.ecm.core.storage.sql.Model;
import org.nuxeo.ecm.core.storage.sql.Node;
import org.nuxeo.ecm.core.storage.sql.Session;
import org.nuxeo.ecm.core.storage.sql.SessionImpl;
/**
* A connection is a handle to the underlying storage. It is returned by the {@link ConnectionFactory} to application
* code.
* <p>
* The actual link to the underlying storage ({@link Session}) is provided by the
* {@link javax.resource.spi.ManagedConnection} which created this {@link javax.resource.cci.Connection}.
*
* @author Florent Guillaume
*/
public class ConnectionImpl implements Session {
private ManagedConnectionImpl managedConnection;
private SessionImpl session;
public ConnectionImpl(ManagedConnectionImpl managedConnection) {
this.managedConnection = managedConnection;
}
/*
* ----- callbacks -----
*/
/**
* Called by {@link ManagedConnectionImpl#associateConnection}.
*/
protected ManagedConnectionImpl getManagedConnection() {
return managedConnection;
}
/**
* Called by {@link ManagedConnectionImpl#associateConnection}.
*/
protected void setManagedConnection(ManagedConnectionImpl managedConnection) {
this.managedConnection = managedConnection;
}
/**
* Called by {@link ManagedConnectionImpl#addConnection}.
*/
protected void associate(SessionImpl session) {
this.session = session;
}
/**
* Called by {@link ManagedConnectionImpl#removeConnection}.
*/
protected void disassociate() {
closeStillOpenQueryResults();
session = null;
}
/*
* ----- javax.resource.cci.Connection -----
*/
protected Throwable closeTrace;
@Override
public void close() throws ResourceException {
if (managedConnection == null) {
IllegalStateException error = new IllegalStateException("connection already closed " + this);
error.addSuppressed(closeTrace);
throw error;
}
try {
managedConnection.close(this);
} finally {
closeTrace = new Throwable("close stack trace");
managedConnection = null;
}
}
@Override
public Interaction createInteraction() throws ResourceException {
throw new UnsupportedOperationException();
}
@Override
public LocalTransaction getLocalTransaction() throws ResourceException {
throw new UnsupportedOperationException();
}
@Override
public ConnectionMetaData getMetaData() throws ResourceException {
throw new UnsupportedOperationException();
}
@Override
public ResultSetInfo getResultSetInfo() throws ResourceException {
throw new UnsupportedOperationException();
}
/*
* ----- org.nuxeo.ecm.core.storage.sql.Session -----
*/
private Session getSession() {
if (session == null) {
throw new NuxeoException("Cannot use closed connection handle: " + this);
}
return session;
}
@Override
public Mapper getMapper() {
return getSession().getMapper();
}
@Override
public boolean isLive() {
return session != null && session.isLive();
}
@Override
public String getRepositoryName() {
return getSession().getRepositoryName();
}
@Override
public Model getModel() {
return getSession().getModel();
}
@Override
public void save() {
getSession().save();
}
@Override
public Node getRootNode() {
return getSession().getRootNode();
}
@Override
public Node getNodeById(Serializable id) {
return getSession().getNodeById(id);
}
@Override
public List<Node> getNodesByIds(List<Serializable> ids) {
return getSession().getNodesByIds(ids);
}
@Override
public Node getNodeByPath(String path, Node node) {
return getSession().getNodeByPath(path, node);
}
@Override
public boolean addMixinType(Node node, String mixin) {
return getSession().addMixinType(node, mixin);
}
@Override
public boolean removeMixinType(Node node, String mixin) {
return getSession().removeMixinType(node, mixin);
}
@Override
public ScrollResult scroll(String query, int batchSize, int keepAliveSeconds) {
return getSession().scroll(query, batchSize, keepAliveSeconds);
}
@Override
public ScrollResult scroll(String scrollId) {
return getSession().scroll(scrollId);
}
@Override
public boolean hasChildNode(Node parent, String name, boolean complexProp) {
return getSession().hasChildNode(parent, name, complexProp);
}
@Override
public Node getChildNode(Node parent, String name, boolean complexProp) {
return getSession().getChildNode(parent, name, complexProp);
}
@Override
public boolean hasChildren(Node parent, boolean complexProp) {
return getSession().hasChildren(parent, complexProp);
}
@Override
public List<Node> getChildren(Node parent, String name, boolean complexProp) {
return getSession().getChildren(parent, name, complexProp);
}
@Override
public Node addChildNode(Node parent, String name, Long pos, String typeName, boolean complexProp) {
return getSession().addChildNode(parent, name, pos, typeName, complexProp);
}
@Override
public Node addChildNode(Serializable id, Node parent, String name, Long pos, String typeName,
boolean complexProp) {
return getSession().addChildNode(id, parent, name, pos, typeName, complexProp);
}
@Override
public void removeNode(Node node) {
getSession().removeNode(node);
}
@Override
public void removePropertyNode(Node node) {
getSession().removePropertyNode(node);
}
@Override
public Node getParentNode(Node node) {
return getSession().getParentNode(node);
}
@Override
public String getPath(Node node) {
return getSession().getPath(node);
}
@Override
public void orderBefore(Node node, Node src, Node dest) {
getSession().orderBefore(node, src, dest);
}
@Override
public Node move(Node source, Node parent, String name) {
return getSession().move(source, parent, name);
}
@Override
public Node copy(Node source, Node parent, String name) {
return getSession().copy(source, parent, name);
}
@Override
public Node checkIn(Node node, String label, String checkinComment) {
return getSession().checkIn(node, label, checkinComment);
}
@Override
public void checkOut(Node node) {
getSession().checkOut(node);
}
@Override
public void restore(Node node, Node version) {
getSession().restore(node, version);
}
@Override
public Node getVersionByLabel(Serializable versionSeriesId, String label) {
return getSession().getVersionByLabel(versionSeriesId, label);
}
@Override
public List<Node> getVersions(Serializable versionSeriesId) {
return getSession().getVersions(versionSeriesId);
}
@Override
public Node getLastVersion(Serializable versionSeriesId) {
return getSession().getLastVersion(versionSeriesId);
}
@Override
public List<Node> getProxies(Node document, Node parent) {
return getSession().getProxies(document, parent);
}
@Override
public void setProxyTarget(Node proxy, Serializable targetId) {
getSession().setProxyTarget(proxy, targetId);
}
@Override
public Node addProxy(Serializable targetId, Serializable versionSeriesId, Node parent, String name, Long pos) {
return getSession().addProxy(targetId, versionSeriesId, parent, name, pos);
}
@Override
public PartialList<Serializable> query(String query, QueryFilter queryFilter, boolean countTotal) {
return getSession().query(query, queryFilter, countTotal);
}
@Override
public PartialList<Serializable> query(String query, String queryType, QueryFilter queryFilter, long countUpTo) {
return getSession().query(query, queryType, queryFilter, countUpTo);
}
@Override
public IterableQueryResult queryAndFetch(String query, String queryType, QueryFilter queryFilter,
Object... params) {
IterableQueryResult result = getSession().queryAndFetch(query, queryType, queryFilter, params);
noteQueryResult(result);
return result;
}
@Override
public IterableQueryResult queryAndFetch(String query, String queryType, QueryFilter queryFilter,
boolean distinctDocuments, Object... params) {
IterableQueryResult result = getSession().queryAndFetch(query, queryType, queryFilter, distinctDocuments,
params);
noteQueryResult(result);
return result;
}
@Override
public PartialList<Map<String,Serializable>> queryProjection(String query, String queryType, QueryFilter queryFilter,
boolean distinctDocuments, long countUpTo, Object... params) {
return getSession().queryProjection(query, queryType, queryFilter, distinctDocuments, countUpTo, params);
}
public static class QueryResultContextException extends Exception {
private static final long serialVersionUID = 1L;
public final IterableQueryResult queryResult;
public QueryResultContextException(IterableQueryResult queryResult) {
super("queryAndFetch call context");
this.queryResult = queryResult;
}
}
protected final Set<QueryResultContextException> queryResults = new HashSet<>();
protected void noteQueryResult(IterableQueryResult result) {
queryResults.add(new QueryResultContextException(result));
}
protected void closeStillOpenQueryResults() {
for (QueryResultContextException context : queryResults) {
if (!context.queryResult.mustBeClosed()) {
continue;
}
try {
context.queryResult.close();
} catch (RuntimeException e) {
LogFactory.getLog(ConnectionImpl.class).error("Cannot close query result", e);
} finally {
LogFactory.getLog(ConnectionImpl.class)
.warn("Closing a query results for you, check stack trace for allocating point", context);
}
}
queryResults.clear();
}
@Override
public LockManager getLockManager() {
return getSession().getLockManager();
}
@Override
public void requireReadAclsUpdate() {
if (session != null) {
session.requireReadAclsUpdate();
}
}
@Override
public void updateReadAcls() {
getSession().updateReadAcls();
}
@Override
public void rebuildReadAcls() {
getSession().rebuildReadAcls();
}
@Override
public Map<String, String> getBinaryFulltext(Serializable id) {
return getSession().getBinaryFulltext(id);
}
@Override
public boolean isChangeTokenEnabled() {
return getSession().isChangeTokenEnabled();
}
}