]> 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) 2007-2012 Argeo GmbH
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 package org.argeo.jackrabbit;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.util.HashSet;
22 import java.util.Map;
23 import java.util.Properties;
24 import java.util.Set;
25 import java.util.TreeSet;
26 import java.util.UUID;
27
28 import javax.jcr.Node;
29 import javax.jcr.Repository;
30 import javax.jcr.RepositoryException;
31 import javax.jcr.Session;
32
33 import org.apache.commons.io.FileUtils;
34 import org.apache.commons.io.IOUtils;
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.jackrabbit.api.JackrabbitRepository;
38 import org.apache.jackrabbit.core.RepositoryImpl;
39 import org.apache.jackrabbit.core.config.RepositoryConfig;
40 import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
41 import org.argeo.jcr.ArgeoJcrException;
42 import org.argeo.jcr.ArgeoNames;
43 import org.argeo.jcr.JcrUtils;
44 import org.springframework.core.io.Resource;
45 import org.springframework.util.SystemPropertyUtils;
46 import org.xml.sax.InputSource;
47
48 /**
49 * Wrapper around a Jackrabbit repository which allows to configure it in Spring
50 * and expose it as a {@link Repository}.
51 */
52 @Deprecated
53 public class JackrabbitContainer extends JackrabbitWrapper {
54 private final static Log log = LogFactory.getLog(JackrabbitContainer.class);
55
56 // local
57 private Resource configuration;
58
59 private Resource variables;
60
61 private RepositoryConfig repositoryConfig;
62 private File homeDirectory;
63 private Boolean inMemory = false;
64
65 /** Migrations to execute (if not already done) */
66 private Set<JackrabbitDataModelMigration> dataModelMigrations = new HashSet<JackrabbitDataModelMigration>();
67
68 /** Straight (non spring) values */
69 private Properties configurationProperties;
70 private InputSource configurationXml;
71
72 /**
73 * Empty constructor, {@link #init()} should be called after properties have
74 * been set
75 */
76 public JackrabbitContainer() {
77 }
78
79 public void init() {
80 // long begin = System.currentTimeMillis();
81
82 if (getRepository() != null)
83 throw new ArgeoJcrException("Cannot be used to wrap another repository");
84 Repository repository = createJackrabbitRepository();
85 super.setRepository(repository);
86
87 // migrate if needed
88 migrate();
89
90 // apply new CND files after migration
91 prepareDataModel();
92
93 // double duration = ((double) (System.currentTimeMillis() - begin)) /
94 // 1000;
95 // if (log.isDebugEnabled())
96 // log.debug("Initialized JCR repository wrapper in " + duration
97 // + " s");
98 }
99
100 /** Actually creates the new repository. */
101 protected Repository createJackrabbitRepository() {
102 long begin = System.currentTimeMillis();
103 InputStream configurationIn = null;
104 Repository repository;
105 try {
106 // temporary
107 if (inMemory && getHomeDirectory().exists()) {
108 FileUtils.deleteDirectory(getHomeDirectory());
109 log.warn("Deleted Jackrabbit home directory " + getHomeDirectory());
110 }
111
112 // process configuration file
113 Properties vars = getConfigurationProperties();
114 vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, getHomeDirectory().getCanonicalPath());
115 InputSource is;
116 if (configurationXml != null)
117 is = configurationXml;
118 else {
119 configurationIn = readConfiguration();
120 is = new InputSource(configurationIn);
121 }
122 repositoryConfig = RepositoryConfig.create(is, vars);
123
124 //
125 // Actual repository creation
126 //
127 repository = RepositoryImpl.create(repositoryConfig);
128
129 double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
130 if (log.isTraceEnabled())
131 log.trace("Created Jackrabbit repository in " + duration + " s, home: " + getHomeDirectory());
132
133 return repository;
134 } catch (Exception e) {
135 throw new ArgeoJcrException("Cannot create Jackrabbit repository " + getHomeDirectory(), e);
136 } finally {
137 IOUtils.closeQuietly(configurationIn);
138 }
139 }
140
141 /** Lazy init. */
142 protected File getHomeDirectory() {
143 try {
144 if (homeDirectory == null) {
145 if (inMemory) {
146 homeDirectory = new File(System.getProperty("java.io.tmpdir") + File.separator
147 + System.getProperty("user.name") + File.separator + "jackrabbit-" + UUID.randomUUID());
148 homeDirectory.mkdirs();
149 // will it work if directory is not empty??
150 homeDirectory.deleteOnExit();
151 }
152 }
153
154 return homeDirectory.getCanonicalFile();
155 } catch (IOException e) {
156 throw new ArgeoJcrException("Cannot get canonical file for " + homeDirectory, e);
157 }
158 }
159
160 /** Executes migrations, if needed. */
161 protected void migrate() {
162 // No migration to perform
163 if (dataModelMigrations.size() == 0)
164 return;
165
166 Boolean restartAndClearCaches = false;
167
168 // migrate data
169 Session session = null;
170 try {
171 session = login();
172 for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
173 dataModelMigrations)) {
174 if (dataModelMigration.migrate(session)) {
175 restartAndClearCaches = true;
176 }
177 }
178 } catch (ArgeoJcrException e) {
179 throw e;
180 } catch (Exception e) {
181 throw new ArgeoJcrException("Cannot migrate", e);
182 } finally {
183 JcrUtils.logoutQuietly(session);
184 }
185
186 // restart repository
187 if (restartAndClearCaches) {
188 Repository repository = getRepository();
189 if (repository instanceof RepositoryImpl) {
190 JackrabbitDataModelMigration.clearRepositoryCaches(((RepositoryImpl) repository).getConfig());
191 }
192 ((JackrabbitRepository) repository).shutdown();
193 createJackrabbitRepository();
194 }
195
196 // set data model version
197 try {
198 session = login();
199 } catch (RepositoryException e) {
200 throw new ArgeoJcrException("Cannot login to migrated repository", e);
201 }
202
203 for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
204 dataModelMigrations)) {
205 try {
206 if (session.itemExists(dataModelMigration.getDataModelNodePath())) {
207 Node dataModelNode = session.getNode(dataModelMigration.getDataModelNodePath());
208 dataModelNode.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
209 dataModelMigration.getTargetVersion());
210 session.save();
211 }
212 } catch (Exception e) {
213 log.error("Cannot set model version", e);
214 }
215 }
216 JcrUtils.logoutQuietly(session);
217
218 }
219
220 /** Shutdown the repository */
221 public void destroy() throws Exception {
222 Repository repository = getRepository();
223 if (repository != null && repository instanceof RepositoryImpl) {
224 long begin = System.currentTimeMillis();
225 ((RepositoryImpl) repository).shutdown();
226 if (inMemory)
227 if (getHomeDirectory().exists()) {
228 FileUtils.deleteDirectory(getHomeDirectory());
229 if (log.isDebugEnabled())
230 log.debug("Deleted Jackrabbit home directory " + getHomeDirectory());
231 }
232 double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
233 if (log.isTraceEnabled())
234 log.trace("Destroyed Jackrabbit repository in " + duration + " s, home: " + getHomeDirectory());
235 }
236 repository = null;
237 }
238
239 public void dispose() {
240 throw new IllegalArgumentException("Call destroy() method instead of dispose()");
241 }
242
243 /*
244 * UTILITIES
245 */
246 /**
247 * Reads the configuration which will initialize a {@link RepositoryConfig}.
248 */
249 protected InputStream readConfiguration() {
250 try {
251 return configuration != null ? configuration.getInputStream() : null;
252 } catch (IOException e) {
253 throw new ArgeoJcrException("Cannot read Jackrabbit configuration " + configuration, e);
254 }
255 }
256
257 /**
258 * Reads the variables which will initialize a {@link Properties}. Returns
259 * null by default, to be overridden.
260 *
261 * @return a new stream or null if no variables available
262 */
263 protected InputStream readVariables() {
264 try {
265 return variables != null ? variables.getInputStream() : null;
266 } catch (IOException e) {
267 throw new ArgeoJcrException("Cannot read Jackrabbit variables " + variables, e);
268 }
269 }
270
271 /**
272 * Resolves ${} placeholders in the provided string. Based on system
273 * properties if no map is provided.
274 */
275 protected String resolvePlaceholders(String string, Map<String, String> variables) {
276 return SystemPropertyUtils.resolvePlaceholders(string);
277 }
278
279 /** Generates the properties to use in the configuration. */
280 protected Properties getConfigurationProperties() {
281 if (configurationProperties != null)
282 return configurationProperties;
283
284 InputStream propsIn = null;
285 Properties vars;
286 try {
287 vars = new Properties();
288 propsIn = readVariables();
289 if (propsIn != null) {
290 vars.load(propsIn);
291 }
292 // resolve system properties
293 for (Object key : vars.keySet()) {
294 // TODO: implement a smarter mechanism to resolve nested ${}
295 String newValue = resolvePlaceholders(vars.getProperty(key.toString()), null);
296 vars.put(key, newValue);
297 }
298 // override with system properties
299 vars.putAll(System.getProperties());
300
301 if (log.isTraceEnabled()) {
302 log.trace("Jackrabbit config variables:");
303 for (Object key : new TreeSet<Object>(vars.keySet()))
304 log.trace(key + "=" + vars.getProperty(key.toString()));
305 }
306
307 } catch (IOException e) {
308 throw new ArgeoJcrException("Cannot read configuration properties", e);
309 } finally {
310 IOUtils.closeQuietly(propsIn);
311 }
312 return vars;
313 }
314
315 /*
316 * FIELDS ACCESS
317 */
318
319 public void setHomeDirectory(File homeDirectory) {
320 this.homeDirectory = homeDirectory;
321 }
322
323 public void setInMemory(Boolean inMemory) {
324 this.inMemory = inMemory;
325 }
326
327 public void setRepository(Repository repository) {
328 throw new ArgeoJcrException("Cannot be used to wrap another repository");
329 }
330
331 public void setDataModelMigrations(Set<JackrabbitDataModelMigration> dataModelMigrations) {
332 this.dataModelMigrations = dataModelMigrations;
333 }
334
335 public void setVariables(Resource variables) {
336 this.variables = variables;
337 }
338
339 public void setConfiguration(Resource configuration) {
340 this.configuration = configuration;
341 }
342
343 public void setConfigurationProperties(Properties configurationProperties) {
344 this.configurationProperties = configurationProperties;
345 }
346
347 public void setConfigurationXml(InputSource configurationXml) {
348 this.configurationXml = configurationXml;
349 }
350
351 }