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}