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