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}