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
;
20 import java
.io
.IOException
;
21 import java
.io
.InputStream
;
22 import java
.util
.HashMap
;
24 import java
.util
.Properties
;
25 import java
.util
.TreeSet
;
26 import java
.util
.UUID
;
28 import javax
.jcr
.Credentials
;
29 import javax
.jcr
.LoginException
;
30 import javax
.jcr
.NoSuchWorkspaceException
;
31 import javax
.jcr
.Repository
;
32 import javax
.jcr
.RepositoryException
;
33 import javax
.jcr
.Session
;
34 import javax
.jcr
.Value
;
36 import org
.apache
.commons
.io
.FileUtils
;
37 import org
.apache
.commons
.io
.IOUtils
;
38 import org
.apache
.commons
.logging
.Log
;
39 import org
.apache
.commons
.logging
.LogFactory
;
40 import org
.apache
.jackrabbit
.core
.RepositoryImpl
;
41 import org
.apache
.jackrabbit
.core
.config
.RepositoryConfig
;
42 import org
.apache
.jackrabbit
.core
.config
.RepositoryConfigurationParser
;
43 import org
.apache
.jackrabbit
.jcr2dav
.Jcr2davRepositoryFactory
;
44 import org
.argeo
.ArgeoException
;
45 import org
.xml
.sax
.InputSource
;
48 * Wrapper around a Jackrabbit repository which allows to simplify configuration
49 * and intercept some actions. It exposes itself as a {@link Repository}.
51 public abstract class JackrabbitWrapper
implements Repository
{
52 private Log log
= LogFactory
.getLog(JackrabbitWrapper
.class);
55 private String uri
= null;
58 private RepositoryConfig repositoryConfig
;
59 private File homeDirectory
;
60 private Boolean inMemory
= false;
63 private Repository repository
;
65 private Boolean autocreateWorkspaces
= false;
68 * Empty constructor, {@link #init()} should be called after properties have
71 public JackrabbitWrapper() {
75 * Reads the configuration which will initialize a {@link RepositoryConfig}.
77 protected abstract InputStream
readConfiguration();
80 * Reads the variables which will initialize a {@link Properties}. Returns
81 * null by default, to be overridden.
83 * @return a new stream or null if no variables available
85 protected InputStream
readVariables() {
90 * Resolves ${} placeholders in the provided string. Based on system
91 * properties if no map is provided.
93 protected abstract String
resolvePlaceholders(String string
,
94 Map
<String
, String
> variables
);
98 long begin
= System
.currentTimeMillis();
100 if (repository
!= null) {
101 // we are just wrapping another repository
104 createJackrabbitRepository();
108 double duration
= ((double) (System
.currentTimeMillis() - begin
)) / 1000;
109 if (log
.isTraceEnabled())
110 log
.trace("Initialized Jackrabbit wrapper in " + duration
+ " s");
114 * Called after initialization of an already existing {@link Repository}
115 * which is being wrapped (e.g. in order to impact its data model). To be
116 * overridden, does nothing by default.
118 protected void postInitWrapped() {
123 * Called after initialization of a new {@link Repository} either local or
124 * remote. To be overridden, does nothing by default.
126 protected void postInitNew() {
130 /** Actually creates the new repository. */
131 protected void createJackrabbitRepository() {
132 long begin
= System
.currentTimeMillis();
133 InputStream configurationIn
= null;
135 if (uri
!= null && !uri
.trim().equals("")) {// remote
136 Map
<String
, String
> params
= new HashMap
<String
, String
>();
138 org
.apache
.jackrabbit
.commons
.JcrUtils
.REPOSITORY_URI
,
140 repository
= new Jcr2davRepositoryFactory()
141 .getRepository(params
);
142 if (repository
== null)
143 throw new ArgeoException("Remote Davex repository " + uri
145 double duration
= ((double) (System
.currentTimeMillis() - begin
)) / 1000;
146 log
.info("Created Jackrabbit repository in " + duration
147 + " s from URI " + uri
);
148 // we assume that the data model of the remote repository has
149 // been properly initialized
151 // force uri to null in order to optimize isRemote()
155 if (inMemory
&& getHomeDirectory().exists()) {
156 FileUtils
.deleteDirectory(getHomeDirectory());
157 log
.warn("Deleted Jackrabbit home directory "
158 + getHomeDirectory());
161 // process configuration file
162 Properties vars
= getConfigurationProperties();
163 configurationIn
= readConfiguration();
165 RepositoryConfigurationParser
.REPOSITORY_HOME_VARIABLE
,
166 getHomeDirectory().getCanonicalPath());
167 repositoryConfig
= RepositoryConfig
.create(new InputSource(
168 configurationIn
), vars
);
171 // Actual repository creation
173 repository
= RepositoryImpl
.create(repositoryConfig
);
175 double duration
= ((double) (System
.currentTimeMillis() - begin
)) / 1000;
176 if (log
.isTraceEnabled())
177 log
.trace("Created Jackrabbit repository in " + duration
178 + " s, home: " + getHomeDirectory());
180 } catch (Exception e
) {
181 throw new ArgeoException("Cannot create Jackrabbit repository "
182 + getHomeDirectory(), e
);
184 IOUtils
.closeQuietly(configurationIn
);
189 protected File
getHomeDirectory() {
191 if (homeDirectory
== null) {
193 homeDirectory
= new File(
194 System
.getProperty("java.io.tmpdir")
196 + System
.getProperty("user.name")
197 + File
.separator
+ "jackrabbit-"
198 + UUID
.randomUUID());
199 homeDirectory
.mkdirs();
200 // will it work if directory is not empty??
201 homeDirectory
.deleteOnExit();
205 return homeDirectory
.getCanonicalFile();
206 } catch (IOException e
) {
207 throw new ArgeoException("Cannot get canonical file for "
212 /** Shutdown the repository */
213 public void destroy() throws Exception
{
214 if (repository
!= null && repository
instanceof RepositoryImpl
) {
215 long begin
= System
.currentTimeMillis();
216 ((RepositoryImpl
) repository
).shutdown();
218 if (getHomeDirectory().exists()) {
219 FileUtils
.deleteDirectory(getHomeDirectory());
220 if (log
.isDebugEnabled())
221 log
.debug("Deleted Jackrabbit home directory "
222 + getHomeDirectory());
224 double duration
= ((double) (System
.currentTimeMillis() - begin
)) / 1000;
225 log
.info("Destroyed Jackrabbit repository in " + duration
226 + " s, home: " + getHomeDirectory());
231 * @deprecated explicitly declare {@link #destroy()} as destroy-method
234 public void dispose() throws Exception
{
235 log
.error("## Declare destroy-method=\"destroy\". in the Jackrabbit container bean");
243 /** Generates the properties to use in the configuration. */
244 protected Properties
getConfigurationProperties() {
245 InputStream propsIn
= null;
248 vars
= new Properties();
249 propsIn
= readVariables();
250 if (propsIn
!= null) {
253 // resolve system properties
254 for (Object key
: vars
.keySet()) {
255 // TODO: implement a smarter mechanism to resolve nested ${}
256 String newValue
= resolvePlaceholders(
257 vars
.getProperty(key
.toString()), null);
258 vars
.put(key
, newValue
);
260 // override with system properties
261 vars
.putAll(System
.getProperties());
263 if (log
.isTraceEnabled()) {
264 log
.trace("Jackrabbit config variables:");
265 for (Object key
: new TreeSet
<Object
>(vars
.keySet()))
266 log
.trace(key
+ "=" + vars
.getProperty(key
.toString()));
269 } catch (IOException e
) {
270 throw new ArgeoException("Cannot read configuration properties", e
);
272 IOUtils
.closeQuietly(propsIn
);
278 * DELEGATED JCR REPOSITORY METHODS
281 public String
getDescriptor(String key
) {
282 return getRepository().getDescriptor(key
);
285 public String
[] getDescriptorKeys() {
286 return getRepository().getDescriptorKeys();
289 /** Central login method */
290 public Session
login(Credentials credentials
, String workspaceName
)
291 throws LoginException
, NoSuchWorkspaceException
,
292 RepositoryException
{
295 session
= getRepository().login(credentials
, workspaceName
);
296 } catch (NoSuchWorkspaceException e
) {
297 if (autocreateWorkspaces
&& workspaceName
!= null)
298 session
= createWorkspaceAndLogsIn(credentials
, workspaceName
);
302 processNewSession(session
);
306 public Session
login() throws LoginException
, RepositoryException
{
307 return login(null, null);
310 public Session
login(Credentials credentials
) throws LoginException
,
311 RepositoryException
{
312 return login(credentials
, null);
315 public Session
login(String workspaceName
) throws LoginException
,
316 NoSuchWorkspaceException
, RepositoryException
{
317 return login(null, workspaceName
);
320 /** Called after a session has been created, does nothing by default. */
321 protected void processNewSession(Session session
) {
324 public Boolean
isRemote() {
328 /** Wraps access to the repository, making sure it is available. */
329 protected Repository
getRepository() {
330 if (repository
== null) {
331 throw new ArgeoException(
332 "No repository initialized."
333 + " Was the init() method called?"
334 + " The dispose() method should also be called on shutdown.");
340 * Logs in to the default workspace, creates the required workspace, logs
341 * out, logs in to the required workspace.
343 protected Session
createWorkspaceAndLogsIn(Credentials credentials
,
344 String workspaceName
) throws RepositoryException
{
345 if (workspaceName
== null)
346 throw new ArgeoException("No workspace specified.");
347 Session session
= getRepository().login(credentials
);
348 session
.getWorkspace().createWorkspace(workspaceName
);
350 return getRepository().login(credentials
, workspaceName
);
353 public boolean isStandardDescriptor(String key
) {
354 return getRepository().isStandardDescriptor(key
);
357 public boolean isSingleValueDescriptor(String key
) {
358 return getRepository().isSingleValueDescriptor(key
);
361 public Value
getDescriptorValue(String key
) {
362 return getRepository().getDescriptorValue(key
);
365 public Value
[] getDescriptorValues(String key
) {
366 return getRepository().getDescriptorValues(key
);
373 public void setHomeDirectory(File homeDirectory
) {
374 this.homeDirectory
= homeDirectory
;
377 public void setInMemory(Boolean inMemory
) {
378 this.inMemory
= inMemory
;
381 public void setUri(String uri
) {
385 public void setRepository(Repository repository
) {
386 this.repository
= repository
;