]> git.argeo.org Git - lgpl/argeo-commons.git/blob - jackrabbit/JackrabbitContainer.java
Prepare next development cycle
[lgpl/argeo-commons.git] / jackrabbit / JackrabbitContainer.java
1 /*
2 * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 package org.argeo.jackrabbit;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.File;
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;
28 import java.util.Map;
29 import java.util.Properties;
30 import java.util.concurrent.Executor;
31
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;
39
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;
61
62 /**
63 * Wrapper around a Jackrabbit repository which allows to configure it in Spring
64 * and expose it as a {@link Repository}.
65 */
66 public class JackrabbitContainer implements InitializingBean, DisposableBean,
67 Repository, ResourceLoaderAware {
68 private Log log = LogFactory.getLog(JackrabbitContainer.class);
69
70 private Resource configuration;
71 private File homeDirectory;
72 private Resource variables;
73
74 private Boolean inMemory = false;
75 private String uri = null;
76
77 private Repository repository;
78
79 private ResourceLoader resourceLoader;
80
81 /** Node type definitions in CND format */
82 private List<String> cndFiles = new ArrayList<String>();
83
84 /** Namespaces to register: key is prefix, value namespace */
85 private Map<String, String> namespaces = new HashMap<String, String>();
86
87 private Boolean autocreateWorkspaces = false;
88
89 private Executor systemExecutor;
90 private Credentials adminCredentials;
91
92 public void afterPropertiesSet() throws Exception {
93 // remote repository
94 if (uri != null && !uri.trim().equals("")) {
95 Map<String, String> params = new HashMap<String, String>();
96 params.put(org.apache.jackrabbit.commons.JcrUtils.REPOSITORY_URI,
97 uri);
98 repository = new Jcr2davRepositoryFactory().getRepository(params);
99 if (repository == null)
100 throw new ArgeoException("Remote Davex repository " + uri
101 + " not found");
102 log.info("Initialized Jackrabbit repository " + repository
103 + " from URI " + uri);
104 // do not perform further initialization since we assume that the
105 // remote repository has been properly configured
106 return;
107 }
108
109 // local repository
110 if (inMemory && homeDirectory.exists()) {
111 FileUtils.deleteDirectory(homeDirectory);
112 log.warn("Deleted Jackrabbit home directory " + homeDirectory);
113 }
114
115 RepositoryConfig config;
116 Properties vars = getConfigurationProperties();
117 InputStream in = configuration.getInputStream();
118 try {
119 vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
120 homeDirectory.getCanonicalPath());
121 config = RepositoryConfig.create(new InputSource(in), vars);
122 } catch (Exception e) {
123 throw new RuntimeException("Cannot read configuration", e);
124 } finally {
125 IOUtils.closeQuietly(in);
126 }
127
128 if (inMemory)
129 repository = new TransientRepository(config);
130 else
131 repository = RepositoryImpl.create(config);
132
133 if (cndFiles != null && cndFiles.size() > 0)
134 importNodeTypeDefinitions(repository);
135
136 log.info("Initialized Jackrabbit repository " + repository + " in "
137 + homeDirectory + " with config " + configuration);
138 }
139
140 protected Properties getConfigurationProperties() {
141 InputStream propsIn = null;
142 Properties vars;
143 try {
144 vars = new Properties();
145 if (variables != null) {
146 propsIn = variables.getInputStream();
147 vars.load(propsIn);
148 }
149 // resolve system properties
150 for (Object key : vars.keySet()) {
151 // TODO: implement a smarter mechanism to resolve nested ${}
152 String newValue = SystemPropertyUtils.resolvePlaceholders(vars
153 .getProperty(key.toString()));
154 vars.put(key, newValue);
155 }
156 // override with system properties
157 vars.putAll(System.getProperties());
158 } catch (IOException e) {
159 throw new ArgeoException("Cannot read configuration properties", e);
160 } finally {
161 IOUtils.closeQuietly(propsIn);
162 }
163 return vars;
164 }
165
166 /**
167 * Import declared node type definitions, trying to update them if they have
168 * changed. In case of failures an error will be logged but no exception
169 * will be thrown.
170 */
171 protected void importNodeTypeDefinitions(final Repository repository) {
172 final Credentials credentialsToUse;
173 if (systemExecutor == null) {
174 if (adminCredentials == null)
175 throw new ArgeoException(
176 "No system executor or admin credentials found");
177 credentialsToUse = adminCredentials;
178 } else {
179 credentialsToUse = null;
180 }
181
182 Runnable action = new Runnable() {
183 public void run() {
184 Reader reader = null;
185 Session session = null;
186 try {
187 session = repository.login(credentialsToUse);
188 processNewSession(session);
189 // Load cnds as resources
190 for (String resUrl : cndFiles) {
191 Resource res = resourceLoader.getResource(resUrl);
192 byte[] arr = IOUtils.toByteArray(res.getInputStream());
193 reader = new InputStreamReader(
194 new ByteArrayInputStream(arr));
195 CndImporter.registerNodeTypes(reader, session, true);
196 }
197 session.save();
198 } catch (Exception e) {
199 log.error(
200 "Cannot import node type definitions " + cndFiles,
201 e);
202 JcrUtils.discardQuietly(session);
203 } finally {
204 IOUtils.closeQuietly(reader);
205 JcrUtils.logoutQuietly(session);
206 }
207 }
208 };
209
210 if (systemExecutor != null)
211 systemExecutor.execute(action);
212 else
213 action.run();
214 }
215
216 public void destroy() throws Exception {
217 if (repository != null) {
218 if (repository instanceof JackrabbitRepository)
219 ((JackrabbitRepository) repository).shutdown();
220 else if (repository instanceof RepositoryImpl)
221 ((RepositoryImpl) repository).shutdown();
222 else if (repository instanceof TransientRepository)
223 ((TransientRepository) repository).shutdown();
224 }
225
226 if (inMemory)
227 if (homeDirectory.exists()) {
228 FileUtils.deleteDirectory(homeDirectory);
229 if (log.isDebugEnabled())
230 log.debug("Deleted Jackrabbit home directory "
231 + homeDirectory);
232 }
233
234 if (uri != null && !uri.trim().equals(""))
235 log.info("Destroyed Jackrabbit repository with uri " + uri);
236 else
237 log.info("Destroyed Jackrabbit repository " + repository + " in "
238 + homeDirectory + " with config " + configuration);
239 }
240
241 // JCR REPOSITORY (delegated)
242 public String getDescriptor(String key) {
243 return repository.getDescriptor(key);
244 }
245
246 public String[] getDescriptorKeys() {
247 return repository.getDescriptorKeys();
248 }
249
250 public Session login() throws LoginException, RepositoryException {
251 Session session = repository.login();
252 processNewSession(session);
253 return session;
254 }
255
256 public Session login(Credentials credentials, String workspaceName)
257 throws LoginException, NoSuchWorkspaceException,
258 RepositoryException {
259 Session session;
260 try {
261 session = repository.login(credentials, workspaceName);
262 } catch (NoSuchWorkspaceException e) {
263 if (autocreateWorkspaces)
264 session = createWorkspaceAndLogsIn(credentials, workspaceName);
265 else
266 throw e;
267 }
268 processNewSession(session);
269 return session;
270 }
271
272 public Session login(Credentials credentials) throws LoginException,
273 RepositoryException {
274 Session session = repository.login(credentials);
275 processNewSession(session);
276 return session;
277 }
278
279 public Session login(String workspaceName) throws LoginException,
280 NoSuchWorkspaceException, RepositoryException {
281 Session session;
282 try {
283 session = repository.login(workspaceName);
284 } catch (NoSuchWorkspaceException e) {
285 if (autocreateWorkspaces)
286 session = createWorkspaceAndLogsIn(null, workspaceName);
287 else
288 throw e;
289 }
290 processNewSession(session);
291 return session;
292 }
293
294 protected synchronized void processNewSession(Session session) {
295 try {
296 NamespaceHelper namespaceHelper = new NamespaceHelper(session);
297 namespaceHelper.registerNamespaces(namespaces);
298
299 } catch (Exception e) {
300 throw new ArgeoException("Cannot process new session", e);
301 }
302 }
303
304 /**
305 * Logs in to the default workspace, creates the required workspace, logs
306 * out, logs in to the required workspace.
307 */
308 protected Session createWorkspaceAndLogsIn(Credentials credentials,
309 String workspaceName) throws RepositoryException {
310 if (workspaceName == null)
311 throw new ArgeoException("No workspace specified.");
312 Session session = repository.login(credentials);
313 session.getWorkspace().createWorkspace(workspaceName);
314 session.logout();
315 return repository.login(credentials, workspaceName);
316 }
317
318 public void setResourceLoader(ResourceLoader resourceLoader) {
319 this.resourceLoader = resourceLoader;
320 }
321
322 public boolean isStandardDescriptor(String key) {
323 return repository.isStandardDescriptor(key);
324 }
325
326 public boolean isSingleValueDescriptor(String key) {
327 return repository.isSingleValueDescriptor(key);
328 }
329
330 public Value getDescriptorValue(String key) {
331 return repository.getDescriptorValue(key);
332 }
333
334 public Value[] getDescriptorValues(String key) {
335 return repository.getDescriptorValues(key);
336 }
337
338 // BEANS METHODS
339 public void setHomeDirectory(File homeDirectory) {
340 this.homeDirectory = homeDirectory;
341 }
342
343 public void setConfiguration(Resource configuration) {
344 this.configuration = configuration;
345 }
346
347 public void setInMemory(Boolean inMemory) {
348 this.inMemory = inMemory;
349 }
350
351 public void setNamespaces(Map<String, String> namespaces) {
352 this.namespaces = namespaces;
353 }
354
355 public void setCndFiles(List<String> cndFiles) {
356 this.cndFiles = cndFiles;
357 }
358
359 public void setVariables(Resource variables) {
360 this.variables = variables;
361 }
362
363 public void setUri(String uri) {
364 this.uri = uri;
365 }
366
367 public void setSystemExecutor(Executor systemExecutor) {
368 this.systemExecutor = systemExecutor;
369 }
370
371 public void setAdminCredentials(Credentials adminCredentials) {
372 this.adminCredentials = adminCredentials;
373 }
374
375 }