]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
b6db6f339864070cdfbfb971785a54ca244acfa0
[tmp/jakarta-migration.git] /
1 /*
2  * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
3  *
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the GNU Lesser General Public License
6  * (LGPL) version 2.1 which accompanies this distribution, and is available at
7  * http://www.gnu.org/licenses/lgpl.html
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * Contributors:
15  *     bstefanescu, jcarsique
16  *
17  * $Id$
18  */
19
20 package org.collectionspace.services.nuxeo.client.java;
21
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.Map.Entry;
26 import java.security.Principal;
27
28 import org.collectionspace.services.common.context.ServiceContext;
29 import org.collectionspace.services.common.repository.RepositoryInstanceWrapperAdvice;
30 import org.collectionspace.services.config.tenant.RepositoryDomainType;
31 import org.nuxeo.ecm.core.api.repository.Repository;
32 import org.nuxeo.ecm.core.api.CoreInstance;
33 import org.nuxeo.ecm.core.api.CoreSession;
34 import org.nuxeo.ecm.core.api.NuxeoPrincipal;
35 import org.nuxeo.ecm.core.api.SystemPrincipal;
36 import org.nuxeo.ecm.core.api.repository.RepositoryManager;
37 import org.nuxeo.runtime.api.Framework;
38 import org.nuxeo.runtime.jtajca.NuxeoContainer;
39 import org.nuxeo.runtime.transaction.TransactionHelper;
40
41 import javax.transaction.TransactionManager;
42 import javax.transaction.UserTransaction;
43
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46 import org.springframework.aop.framework.ProxyFactory;
47
48 /**
49  * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
50  *
51  */
52 public final class NuxeoClientEmbedded {
53
54         private Logger logger = LoggerFactory.getLogger(NuxeoClientEmbedded.class);
55         
56     private final HashMap<String, CoreSessionInterface> repositoryInstances;
57
58     private RepositoryManager repositoryMgr;
59
60     private static final NuxeoClientEmbedded instance = new NuxeoClientEmbedded();
61         
62     /**
63      * Constructs a new NuxeoClient. NOTE: Using {@link #getInstance()} instead
64      * of this constructor is recommended.
65      */
66     private NuxeoClientEmbedded() {
67         repositoryInstances = new HashMap<String, CoreSessionInterface>();
68     }
69     
70     public static NuxeoClientEmbedded getInstance() {
71         return instance;
72     }
73
74     public synchronized void tryDisconnect() throws Exception {
75         doDisconnect();
76     }
77
78     private void doDisconnect() throws Exception {
79         // close the open Nuxeo repository sessions if any
80         Iterator<Entry<String, CoreSessionInterface>> it = repositoryInstances.entrySet().iterator();
81         while (it.hasNext()) {
82             Entry<String, CoreSessionInterface> repo = it.next();
83             try {
84                 repo.getValue().close();
85             } catch (Exception e) {
86                 logger.debug("Error while trying to close " + repo, e);
87             }
88             it.remove();
89         }
90
91         repositoryMgr = null;
92     }
93
94     public RepositoryManager getRepositoryManager() throws Exception {
95         if (repositoryMgr == null) {
96             repositoryMgr = Framework.getService(RepositoryManager.class);
97         }
98         return repositoryMgr;
99     }
100
101     /**
102      * Gets the repositories available on the connected server.
103      *
104      * @return the repositories
105      */
106     public Repository[] getRepositories() throws Exception {
107         Collection<Repository> repos = getRepositoryManager().getRepositories();
108         return repos.toArray(new Repository[repos.size()]);
109     }
110
111     public Repository getDefaultRepository() throws Exception {
112         return getRepositoryManager().getDefaultRepository();
113     }
114
115     public Repository getRepository(String name) throws Exception {
116         return getRepositoryManager().getRepository(name);
117     }
118
119     /*
120      * Open a Nuxeo repo session using the passed in repoDomain and use the default tx timeout period
121      */
122     public CoreSessionInterface openRepository(RepositoryDomainType repoDomain) throws Exception {
123         return openRepository(repoDomain.getRepositoryName(), ServiceContext.DEFAULT_TX_TIMEOUT);
124     }
125     
126     /*
127      * Open a Nuxeo repo session using the passed in repoDomain and use the default tx timeout period
128      */
129     public CoreSessionInterface openRepository(String repoName) throws Exception {
130         return openRepository(repoName, ServiceContext.DEFAULT_TX_TIMEOUT);
131     }    
132
133     public CoreSessionInterface openRepository(String repoName, int timeoutSeconds) throws Exception {
134         CoreSessionInterface result = null;
135         
136         //
137         // If the called passed in a custom timeout setting, use it to configure Nuxeo's transaction manager.
138         //
139         if (timeoutSeconds > 0) {
140                 TransactionManager transactionMgr = TransactionHelper.lookupTransactionManager();
141             TransactionManager tm = NuxeoContainer.getTransactionManager();
142             if (logger.isDebugEnabled()) {
143                 if (tm != transactionMgr) {
144                         logger.debug("TransactionHelper's manager is different than NuxeoContainer's.");
145                 }
146             }
147                 
148                 transactionMgr.setTransactionTimeout(timeoutSeconds); // For the current thread only
149                 if (logger.isInfoEnabled()) {
150                         logger.info(String.format("Changing current request's transaction timeout period to %d seconds",
151                                         timeoutSeconds));
152                 }
153         }
154         
155         //
156         // Start a new Nuxeo transaction
157         //
158         boolean startedTransaction = false;
159         if (TransactionHelper.isTransactionActive() == false) {
160                 startedTransaction = TransactionHelper.startTransaction();
161                 if (startedTransaction == false) {
162                         String errMsg = "Could not start a Nuxeo transaction with the TransactionHelper class.";
163                         logger.error(errMsg);
164                         throw new Exception(errMsg);
165                 }
166         } else {
167                 logger.warn("A request to start a new transaction was made, but a transaction is already open.");
168         }
169         
170         //
171         // From the repository name that the caller passed in, get an instance of Nuxeo's Repository class.
172         // The Repository class is just a metadata description of the repository.
173         //
174         Repository repository;
175         if (repoName != null) {
176                 repository = getRepositoryManager().getRepository(repoName);
177         } else {
178                 repository = getRepositoryManager().getDefaultRepository();
179                 logger.warn(String.format("Using default repository '%s' because no name was specified.", repository.getName()));
180         }
181         
182         //
183         // Using the Repository class, get a Spring AOP proxied instance.  We use Spring AOP to "wrap" all calls to the
184         // Nuxeo repository so we can check for network related failures and perform a series of retries.
185         //
186         if (repository != null) {
187             result = getCoreSessionWrapper(repository);
188                 logger.trace(String.format("A new transaction was started on thread '%d' : %s.",
189                                 Thread.currentThread().getId(), startedTransaction ? "true" : "false"));
190                 logger.trace(String.format("Added a new repository instance to our repo list.  Current count is now: %d",
191                                 repositoryInstances.size()));
192         } else {
193                 String errMsg = String.format("Could not open a session to the Nuxeo repository='%s'", repoName);
194                 logger.error(errMsg);
195                 throw new Exception(errMsg);
196         }       
197         
198         
199         return result;
200     }
201     
202     //
203     // Returns a proxied interface to a Nuxeo repository instance.  Our proxy uses Spring AOP to
204     // wrap each call to the Nuxeo repo with code that catches network related errors/exceptions and
205     // re-attempts the calls to see if it recovers.
206     //
207     private CoreSessionInterface getAOPProxy(CoreSession repositoryInstance) {
208         CoreSessionInterface result = null;
209         
210         try {
211                         ProxyFactory factory = new ProxyFactory(new CoreSessionWrapper(repositoryInstance));
212                         factory.addAdvice(new RepositoryInstanceWrapperAdvice());
213                         factory.setExposeProxy(true);
214                         result = (CoreSessionInterface)factory.getProxy();
215         } catch (Exception e) {
216                 logger.error("Could not create AOP proxy for: " + CoreSessionWrapper.class.getName(), e);
217         }
218         
219         return result;
220     }
221     
222         private Principal getSystemPrincipal() {
223                 NuxeoPrincipal principal = new SystemPrincipal(null);
224                 return principal;
225         }
226
227     /*
228      * From the Repository object (a description of the repository), get repository instance wrapper.  Our wrapper
229      * will using the Spring AOP mechanism to intercept all calls to the repository.  We will wrap all the calls to the
230      * Nuxeo repository and check for network related failures.  We will retry all calls to the Nuxeo repo that fail because
231      * of network erros.
232      */
233     private CoreSessionInterface getCoreSessionWrapper(Repository repository) throws Exception {
234         CoreSessionInterface result = null;
235                 
236         CoreSession coreSession  = CoreInstance.openCoreSession(repository.getName(), getSystemPrincipal());  // A Nuxeo repo instance handler proxy
237         
238         if (coreSession != null) {
239                 result = this.getAOPProxy(coreSession);  // This is our AOP proxy
240                 if (result != null) {
241                         String key = result.getSessionId();
242                         repositoryInstances.put(key, result);
243                 } else {
244                         String errMsg = String.format("Could not instantiate a Spring AOP proxy for class '%s'.",
245                                         CoreSessionWrapper.class.getName());
246                         logger.error(errMsg);
247                         throw new Exception(errMsg);
248                 }
249         } else {
250                 String errMsg = String.format("Could not create a new repository instance for '%s' repository.", repository.getName());
251                 logger.error(errMsg);
252                 throw new Exception(errMsg);
253         }
254         
255         return result;
256     }
257
258     public void releaseRepository(CoreSessionInterface repoSession) throws Exception {
259         String key = repoSession.getSessionId();
260         String name = repoSession.getRepositoryName();
261
262         try {
263                 repoSession.save();
264                 repoSession.close();
265         } catch (Exception e) {
266                 String errMsg = String.format("Possible data loss.  Could not save and/or close the Nuxeo repository name = '%s'.",
267                                 name);
268                 logger.error(errMsg, e);
269                 throw e;
270         } finally {
271                 CoreSessionInterface wasRemoved = repositoryInstances.remove(key);
272             if (logger.isTraceEnabled()) {
273                 if (wasRemoved != null) {
274                         logger.trace("Removed a repository instance from our repo list.  Current count is now: "
275                                         + repositoryInstances.size());
276                 } else {
277                         logger.trace("Could not remove a repository instance from our repo list.  Current count is now: "
278                                         + repositoryInstances.size());
279                 }
280             }            
281             //
282             // Last but not least, try to commit the current Nuxeo-related transaction.
283             //
284             if (TransactionHelper.isTransactionActiveOrMarkedRollback() == true) {
285                 TransactionHelper.commitOrRollbackTransaction();
286                 UserTransaction ut = TransactionHelper.lookupUserTransaction();
287                 TransactionManager tm = TransactionHelper.lookupTransactionManager();                   
288                 logger.trace(String.format("Transaction closed on thread '%d'", Thread.currentThread().getId()));
289             }
290         }
291     }    
292 }