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