2 * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org
.argeo
.jackrabbit
;
19 import java
.io
.ByteArrayInputStream
;
21 import java
.io
.IOException
;
22 import java
.io
.InputStream
;
23 import java
.io
.InputStreamReader
;
24 import java
.io
.Reader
;
25 import java
.util
.ArrayList
;
26 import java
.util
.HashMap
;
27 import java
.util
.List
;
29 import java
.util
.Properties
;
30 import java
.util
.concurrent
.Executor
;
32 import javax
.jcr
.Credentials
;
33 import javax
.jcr
.LoginException
;
34 import javax
.jcr
.NoSuchWorkspaceException
;
35 import javax
.jcr
.Repository
;
36 import javax
.jcr
.RepositoryException
;
37 import javax
.jcr
.Session
;
38 import javax
.jcr
.Value
;
40 import org
.apache
.commons
.io
.FileUtils
;
41 import org
.apache
.commons
.io
.IOUtils
;
42 import org
.apache
.commons
.logging
.Log
;
43 import org
.apache
.commons
.logging
.LogFactory
;
44 import org
.apache
.jackrabbit
.api
.JackrabbitRepository
;
45 import org
.apache
.jackrabbit
.commons
.NamespaceHelper
;
46 import org
.apache
.jackrabbit
.commons
.cnd
.CndImporter
;
47 import org
.apache
.jackrabbit
.core
.RepositoryImpl
;
48 import org
.apache
.jackrabbit
.core
.TransientRepository
;
49 import org
.apache
.jackrabbit
.core
.config
.RepositoryConfig
;
50 import org
.apache
.jackrabbit
.core
.config
.RepositoryConfigurationParser
;
51 import org
.apache
.jackrabbit
.jcr2dav
.Jcr2davRepositoryFactory
;
52 import org
.argeo
.ArgeoException
;
53 import org
.argeo
.jcr
.JcrUtils
;
54 import org
.springframework
.beans
.factory
.DisposableBean
;
55 import org
.springframework
.beans
.factory
.InitializingBean
;
56 import org
.springframework
.context
.ResourceLoaderAware
;
57 import org
.springframework
.core
.io
.Resource
;
58 import org
.springframework
.core
.io
.ResourceLoader
;
59 import org
.springframework
.util
.SystemPropertyUtils
;
60 import org
.xml
.sax
.InputSource
;
63 * Wrapper around a Jackrabbit repository which allows to configure it in Spring
64 * and expose it as a {@link Repository}.
66 public class JackrabbitContainer
implements InitializingBean
, DisposableBean
,
67 Repository
, ResourceLoaderAware
{
68 private Log log
= LogFactory
.getLog(JackrabbitContainer
.class);
70 private Resource configuration
;
71 private File homeDirectory
;
72 private Resource variables
;
74 private Boolean inMemory
= false;
75 private String uri
= null;
77 private Repository repository
;
79 private ResourceLoader resourceLoader
;
81 /** Node type definitions in CND format */
82 private List
<String
> cndFiles
= new ArrayList
<String
>();
84 /** Namespaces to register: key is prefix, value namespace */
85 private Map
<String
, String
> namespaces
= new HashMap
<String
, String
>();
87 private Boolean autocreateWorkspaces
= false;
89 private Executor systemExecutor
;
90 private Credentials adminCredentials
;
92 // transition from legacy spring approach
93 private Boolean alreadyInitialized
= false;
94 private Boolean alreadyDisposed
= false;
96 /** @deprecated explicitly declare {@link #init()} as init-method instead. */
97 public void afterPropertiesSet() throws Exception
{
98 if (!alreadyInitialized
) {
99 log
.warn("## If not already done,"
100 + " declare init-method=\"init\".");
105 public void init() throws Exception
{
107 alreadyInitialized
= true;
110 protected void initImpl() throws Exception
{
111 if (repository
!= null) {
112 // we are just wrapping another repository
113 importNodeTypeDefinitions(repository
);
118 if (uri
!= null && !uri
.trim().equals("")) {
119 Map
<String
, String
> params
= new HashMap
<String
, String
>();
120 params
.put(org
.apache
.jackrabbit
.commons
.JcrUtils
.REPOSITORY_URI
,
122 repository
= new Jcr2davRepositoryFactory().getRepository(params
);
123 if (repository
== null)
124 throw new ArgeoException("Remote Davex repository " + uri
126 log
.info("Initialized Jackrabbit repository " + repository
127 + " from URI " + uri
);
128 // do not perform further initialization since we assume that the
129 // remote repository has been properly configured
134 if (inMemory
&& homeDirectory
.exists()) {
135 FileUtils
.deleteDirectory(homeDirectory
);
136 log
.warn("Deleted Jackrabbit home directory " + homeDirectory
);
139 RepositoryConfig config
;
140 Properties vars
= getConfigurationProperties();
141 InputStream in
= configuration
.getInputStream();
143 vars
.put(RepositoryConfigurationParser
.REPOSITORY_HOME_VARIABLE
,
144 homeDirectory
.getCanonicalPath());
145 config
= RepositoryConfig
.create(new InputSource(in
), vars
);
146 } catch (Exception e
) {
147 throw new RuntimeException("Cannot read configuration", e
);
149 IOUtils
.closeQuietly(in
);
153 repository
= new TransientRepository(config
);
155 repository
= RepositoryImpl
.create(config
);
157 if (cndFiles
!= null && cndFiles
.size() > 0)
158 importNodeTypeDefinitions(repository
);
160 log
.info("Initialized Jackrabbit repository " + repository
+ " in "
161 + homeDirectory
+ " with config " + configuration
);
165 * @deprecated explicitly declare {@link #dispose()} as destroy-method
168 public void destroy() throws Exception
{
169 if (!alreadyDisposed
) {
170 log
.warn("## If not already done,"
171 + " declare destroy-method=\"dispose\".");
176 public void dispose() throws Exception
{
178 alreadyDisposed
= true;
181 protected void disposeImpl() throws Exception
{
182 if (repository
!= null) {
183 if (repository
instanceof JackrabbitRepository
)
184 ((JackrabbitRepository
) repository
).shutdown();
185 else if (repository
instanceof RepositoryImpl
)
186 ((RepositoryImpl
) repository
).shutdown();
187 else if (repository
instanceof TransientRepository
)
188 ((TransientRepository
) repository
).shutdown();
192 if (homeDirectory
.exists()) {
193 FileUtils
.deleteDirectory(homeDirectory
);
194 if (log
.isDebugEnabled())
195 log
.debug("Deleted Jackrabbit home directory "
199 if (uri
!= null && !uri
.trim().equals(""))
200 log
.info("Destroyed Jackrabbit repository with uri " + uri
);
202 log
.info("Destroyed Jackrabbit repository " + repository
+ " in "
203 + homeDirectory
+ " with config " + configuration
);
206 protected Properties
getConfigurationProperties() {
207 InputStream propsIn
= null;
210 vars
= new Properties();
211 if (variables
!= null) {
212 propsIn
= variables
.getInputStream();
215 // resolve system properties
216 for (Object key
: vars
.keySet()) {
217 // TODO: implement a smarter mechanism to resolve nested ${}
218 String newValue
= SystemPropertyUtils
.resolvePlaceholders(vars
219 .getProperty(key
.toString()));
220 vars
.put(key
, newValue
);
222 // override with system properties
223 vars
.putAll(System
.getProperties());
224 } catch (IOException e
) {
225 throw new ArgeoException("Cannot read configuration properties", e
);
227 IOUtils
.closeQuietly(propsIn
);
233 * Import declared node type definitions, trying to update them if they have
234 * changed. In case of failures an error will be logged but no exception
237 protected void importNodeTypeDefinitions(final Repository repository
) {
238 final Credentials credentialsToUse
= null;
239 // if (systemExecutor == null) {
240 // if (adminCredentials == null) {
241 // log.error("No system executor or admin credentials found,"
242 // + " cannot import node types");
245 // credentialsToUse = adminCredentials;
247 // credentialsToUse = null;
250 Runnable action
= new Runnable() {
252 Reader reader
= null;
253 Session session
= null;
255 session
= repository
.login(credentialsToUse
);
256 processNewSession(session
);
257 // Load cnds as resources
258 for (String resUrl
: cndFiles
) {
259 Resource res
= resourceLoader
.getResource(resUrl
);
260 byte[] arr
= IOUtils
.toByteArray(res
.getInputStream());
261 reader
= new InputStreamReader(
262 new ByteArrayInputStream(arr
));
263 CndImporter
.registerNodeTypes(reader
, session
, true);
266 } catch (Exception e
) {
268 "Cannot import node type definitions " + cndFiles
,
270 JcrUtils
.discardQuietly(session
);
272 IOUtils
.closeQuietly(reader
);
273 JcrUtils
.logoutQuietly(session
);
278 if (systemExecutor
!= null)
279 systemExecutor
.execute(action
);
284 // JCR REPOSITORY (delegated)
285 public String
getDescriptor(String key
) {
286 return repository
.getDescriptor(key
);
289 public String
[] getDescriptorKeys() {
290 return repository
.getDescriptorKeys();
293 public Session
login() throws LoginException
, RepositoryException
{
294 Session session
= repository
.login();
295 processNewSession(session
);
299 public Session
login(Credentials credentials
, String workspaceName
)
300 throws LoginException
, NoSuchWorkspaceException
,
301 RepositoryException
{
304 session
= repository
.login(credentials
, workspaceName
);
305 } catch (NoSuchWorkspaceException e
) {
306 if (autocreateWorkspaces
)
307 session
= createWorkspaceAndLogsIn(credentials
, workspaceName
);
311 processNewSession(session
);
315 public Session
login(Credentials credentials
) throws LoginException
,
316 RepositoryException
{
317 Session session
= repository
.login(credentials
);
318 processNewSession(session
);
322 public Session
login(String workspaceName
) throws LoginException
,
323 NoSuchWorkspaceException
, RepositoryException
{
326 session
= repository
.login(workspaceName
);
327 } catch (NoSuchWorkspaceException e
) {
328 if (autocreateWorkspaces
)
329 session
= createWorkspaceAndLogsIn(null, workspaceName
);
333 processNewSession(session
);
337 protected synchronized void processNewSession(Session session
) {
339 NamespaceHelper namespaceHelper
= new NamespaceHelper(session
);
340 namespaceHelper
.registerNamespaces(namespaces
);
341 } catch (Exception e
) {
342 throw new ArgeoException("Cannot process new session", e
);
347 * Logs in to the default workspace, creates the required workspace, logs
348 * out, logs in to the required workspace.
350 protected Session
createWorkspaceAndLogsIn(Credentials credentials
,
351 String workspaceName
) throws RepositoryException
{
352 if (workspaceName
== null)
353 throw new ArgeoException("No workspace specified.");
354 Session session
= repository
.login(credentials
);
355 session
.getWorkspace().createWorkspace(workspaceName
);
357 return repository
.login(credentials
, workspaceName
);
360 public void setResourceLoader(ResourceLoader resourceLoader
) {
361 this.resourceLoader
= resourceLoader
;
364 public boolean isStandardDescriptor(String key
) {
365 return repository
.isStandardDescriptor(key
);
368 public boolean isSingleValueDescriptor(String key
) {
369 return repository
.isSingleValueDescriptor(key
);
372 public Value
getDescriptorValue(String key
) {
373 return repository
.getDescriptorValue(key
);
376 public Value
[] getDescriptorValues(String key
) {
377 return repository
.getDescriptorValues(key
);
381 public void setHomeDirectory(File homeDirectory
) {
382 this.homeDirectory
= homeDirectory
;
385 public void setConfiguration(Resource configuration
) {
386 this.configuration
= configuration
;
389 public void setInMemory(Boolean inMemory
) {
390 this.inMemory
= inMemory
;
393 public void setNamespaces(Map
<String
, String
> namespaces
) {
394 this.namespaces
= namespaces
;
397 public void setCndFiles(List
<String
> cndFiles
) {
398 this.cndFiles
= cndFiles
;
401 public void setVariables(Resource variables
) {
402 this.variables
= variables
;
405 public void setUri(String uri
) {
409 public void setSystemExecutor(Executor systemExecutor
) {
410 this.systemExecutor
= systemExecutor
;
413 public void setAdminCredentials(Credentials adminCredentials
) {
414 this.adminCredentials
= adminCredentials
;
417 public void setRepository(Repository repository
) {
418 this.repository
= repository
;