1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.jackrabbit;
35
36 import info.magnolia.cms.core.FileSystemHelper;
37 import info.magnolia.cms.core.SystemProperty;
38 import info.magnolia.init.MagnoliaConfigurationProperties;
39 import info.magnolia.objectfactory.Components;
40 import info.magnolia.repository.Provider;
41 import info.magnolia.repository.RepositoryNotInitializedException;
42 import info.magnolia.repository.definition.RepositoryDefinition;
43
44 import java.io.File;
45 import java.io.FileInputStream;
46 import java.io.FileNotFoundException;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.io.InputStreamReader;
50 import java.lang.reflect.InvocationTargetException;
51 import java.lang.reflect.Method;
52 import java.util.Hashtable;
53 import java.util.Iterator;
54 import java.util.Map;
55
56 import javax.inject.Inject;
57 import javax.jcr.NamespaceException;
58 import javax.jcr.Repository;
59 import javax.jcr.RepositoryException;
60 import javax.jcr.Session;
61 import javax.jcr.SimpleCredentials;
62 import javax.jcr.Workspace;
63 import javax.jcr.nodetype.NoSuchNodeTypeException;
64 import javax.jcr.nodetype.NodeTypeManager;
65 import javax.naming.Context;
66 import javax.naming.InitialContext;
67 import javax.naming.NameNotFoundException;
68 import javax.naming.NamingException;
69 import javax.xml.transform.TransformerFactoryConfigurationError;
70
71 import org.apache.commons.beanutils.PropertyUtils;
72 import org.apache.commons.io.IOUtils;
73 import org.apache.commons.lang3.ArrayUtils;
74 import org.apache.commons.lang3.JavaVersion;
75 import org.apache.commons.lang3.StringUtils;
76 import org.apache.commons.lang3.SystemUtils;
77 import org.apache.jackrabbit.commons.cnd.CndImporter;
78 import org.apache.jackrabbit.commons.cnd.ParseException;
79 import org.apache.jackrabbit.core.WorkspaceImpl;
80 import org.apache.jackrabbit.core.jndi.RegistryHelper;
81 import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
82 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
83 import org.apache.jackrabbit.core.nodetype.xml.NodeTypeReader;
84 import org.apache.jackrabbit.spi.Name;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
87
88
89
90
91
92 public class ProviderImpl implements Provider {
93
94
95
96
97 private static final String MAGNOLIA_CLUSTERID_PROPERTY = "magnolia.clusterid";
98
99
100
101
102 private static final String JACKRABBIT_CLUSTER_ID_PROPERTY = "org.apache.jackrabbit.core.cluster.node_id";
103
104 private static final Logger log = LoggerFactory.getLogger(ProviderImpl.class);
105
106 private static final String CONFIG_FILENAME_KEY = "configFile";
107
108 private static final String REPOSITORY_HOME_KEY = "repositoryHome";
109
110 private static final String CONTEXT_FACTORY_CLASS_KEY = "contextFactoryClass";
111
112 private static final String PROVIDER_URL_KEY = "providerURL";
113
114 private static final String BIND_NAME_KEY = "bindName";
115
116 private static final String MGNL_NODETYPES = "/mgnl-nodetypes/magnolia-nodetypes.cnd";
117
118 private static final String CUSTOM_NODETYPES = "customNodeTypes";
119
120
121 private RepositoryDefinition repositoryMapping;
122
123 private Repository repository;
124
125 private String bindName;
126
127 private Hashtable<String, Object> jndiEnv;
128
129 private static final String REPO_HOME_PREFIX = "${repository.home}";
130
131 private static final int REPO_HOME_SUFIX_LEN = REPO_HOME_PREFIX.length();
132
133 private static final String sysRepositoryHome = System.getProperty("repository.home");
134
135 private static final String sysRepositoryHomes = System.getProperty("repository.homes");
136
137 private final FileSystemHelper fileSystemHelper;
138 private final MagnoliaConfigurationProperties configurationProperties;
139
140 @Inject
141 public ProviderImpl(FileSystemHelper fileSystemHelper, MagnoliaConfigurationProperties configurationProperties) {
142 this.fileSystemHelper = fileSystemHelper;
143 this.configurationProperties = configurationProperties;
144 }
145
146
147
148
149 @Deprecated
150 public ProviderImpl(FileSystemHelper fileSystemHelper) {
151 this(fileSystemHelper, Components.getComponent(MagnoliaConfigurationProperties.class));
152 }
153
154
155
156
157 @Deprecated
158 public ProviderImpl() {
159 this(Components.getComponent(FileSystemHelper.class), Components.getComponent(MagnoliaConfigurationProperties.class));
160 }
161
162
163
164
165
166
167
168 private String getRepositoryHome(final String repositoryHome) {
169 boolean relocate = false;
170 String tmp = repositoryHome;
171 if (repositoryHome.startsWith(REPOSITORY_HOME_KEY)) {
172 tmp = repositoryHome.substring(REPO_HOME_SUFIX_LEN);
173 relocate = true;
174 }
175
176
177
178 if (sysRepositoryHome != null && relocate) {
179 return sysRepositoryHome + File.separator + tmp;
180 }
181
182
183
184
185 if (sysRepositoryHomes != null) {
186 return sysRepositoryHomes + File.separator + tmp;
187 }
188
189
190
191
192 return fileSystemHelper.getAbsoluteFileSystemPath(tmp);
193 }
194
195
196
197
198 @Override
199 public void init(RepositoryDefinition repositoryMapping) throws RepositoryNotInitializedException {
200 checkXmlSettings();
201
202 this.repositoryMapping = repositoryMapping;
203
204 Map params = this.repositoryMapping.getParameters();
205 String configFile = (String) params.get(CONFIG_FILENAME_KEY);
206 configFile = fileSystemHelper.getAbsoluteFileSystemPath(configFile);
207 String repositoryHome = (String) params.get(REPOSITORY_HOME_KEY);
208 repositoryHome = getRepositoryHome(repositoryHome);
209
210
211 try {
212 File repoHomeFile = new File(repositoryHome);
213 repositoryHome = repoHomeFile.getCanonicalPath();
214 } catch (IOException e1) {
215
216 }
217
218 String clusterid = SystemProperty.getProperty(MAGNOLIA_CLUSTERID_PROPERTY);
219 if (StringUtils.isNotBlank(clusterid)) {
220 System.setProperty(JACKRABBIT_CLUSTER_ID_PROPERTY, clusterid);
221 }
222
223
224 clusterid = System.getProperty(JACKRABBIT_CLUSTER_ID_PROPERTY);
225
226 log.info("Loading repository at {} (config file: {}) - cluster id: \"{}\"", repositoryHome, configFile, StringUtils.defaultString(clusterid, "<unset>"));
227
228 bindName = (String) params.get(BIND_NAME_KEY);
229 jndiEnv = new Hashtable<String, Object>();
230 jndiEnv.put(Context.INITIAL_CONTEXT_FACTORY, params.get(CONTEXT_FACTORY_CLASS_KEY));
231 jndiEnv.put(Context.PROVIDER_URL, params.get(PROVIDER_URL_KEY));
232
233 try {
234 InitialContext ctx = new InitialContext(jndiEnv);
235
236 try {
237 this.repository = (Repository) ctx.lookup(bindName);
238 } catch (NameNotFoundException ne) {
239 log.debug("No JNDI bound Repository found with name {}, trying to initialize a new Repository", bindName);
240 RegistryHelper.registerRepository(ctx, bindName, configFile, repositoryHome, true);
241 this.repository = (Repository) ctx.lookup(bindName);
242 }
243 this.validateWorkspaces();
244 } catch (NamingException e) {
245 log.error("Unable to initialize repository: {}", e.getMessage(), e);
246 throw new RepositoryNotInitializedException(e);
247 } catch (RepositoryException e) {
248 log.error("Unable to initialize repository: {}", e.getMessage(), e);
249 throw new RepositoryNotInitializedException(e);
250 } catch (TransformerFactoryConfigurationError e) {
251 log.error("Unable to initialize repository: {}", e.getMessage(), e);
252 throw new RepositoryNotInitializedException(e);
253 }
254 }
255
256 @Override
257 public void shutdownRepository() {
258 log.info("Shutting down repository bound to '{}'", bindName);
259
260 try {
261 Context ctx = new InitialContext(jndiEnv);
262 RegistryHelper.unregisterRepository(ctx, bindName);
263 } catch (NamingException e) {
264 log.warn("Unable to shutdown repository {}: {}", bindName, e.getMessage(), e);
265 } catch (Throwable e) {
266 log.error("Failed to shutdown repository {}: {}", bindName, e.getMessage(), e);
267 }
268 }
269
270 @Override
271 public Repository getUnderlyingRepository() throws RepositoryNotInitializedException {
272 if (this.repository == null) {
273 throw new RepositoryNotInitializedException("Null repository");
274 }
275 return this.repository;
276 }
277
278
279
280
281 @Override
282 public void registerNamespace(String namespacePrefix, String uri, Workspace workspace) throws RepositoryException {
283 try {
284 workspace.getNamespaceRegistry().getURI(namespacePrefix);
285 } catch (NamespaceException e) {
286 log.debug(e.getMessage());
287
288 log.info("Registering prefix [{}] with URI {}", namespacePrefix, uri);
289 workspace.getNamespaceRegistry().registerNamespace(namespacePrefix, uri);
290 }
291 }
292
293
294
295
296 @Override
297 public void unregisterNamespace(String prefix, Workspace workspace) throws RepositoryException {
298 workspace.getNamespaceRegistry().unregisterNamespace(prefix);
299 }
300
301
302
303
304 @Override
305 public void registerNodeTypes() throws RepositoryException {
306 registerNodeTypes(StringUtils.EMPTY);
307 }
308
309
310
311
312 @Override
313 public void registerNodeTypes(String path) throws RepositoryException {
314 if (StringUtils.isEmpty(path)) {
315 path = this.repositoryMapping.getParameters().get(CUSTOM_NODETYPES);
316 }
317
318 InputStream configuration = getNodeTypeDefinition(path);
319 if (StringUtils.isEmpty(path) || path.endsWith(".cnd")) {
320 SimpleCredentials credentials = new SimpleCredentials(
321 configurationProperties.getProperty(MagnoliaConfigurationProperties.MAGNOLIA_JACKRABBIT_USER_PROPERTY),
322 configurationProperties.getProperty(MagnoliaConfigurationProperties.MAGNOLIA_JACKRABBIT_PASSWORD_PROPERTY).toCharArray());
323 Session jcrSession = this.repository.login(credentials);
324
325 try {
326 CndImporter.registerNodeTypes(new InputStreamReader(configuration), jcrSession);
327 jcrSession.logout();
328 } catch (ParseException e) {
329 log.warn("Failed to register nodetypes from {}: ", StringUtils.defaultIfEmpty(path, MGNL_NODETYPES));
330 } catch (IOException e) {
331 log.warn("Failed to register nodetypes from {}: ", StringUtils.defaultIfEmpty(path, MGNL_NODETYPES));
332 }
333 } else {
334 this.registerNodeTypes(configuration);
335 }
336 }
337
338
339
340
341 @Override
342 public void registerNodeTypes(InputStream xmlStream) throws RepositoryException {
343 SimpleCredentials credentials = new SimpleCredentials(
344 configurationProperties.getProperty(MagnoliaConfigurationProperties.MAGNOLIA_JACKRABBIT_USER_PROPERTY),
345 configurationProperties.getProperty(MagnoliaConfigurationProperties.MAGNOLIA_JACKRABBIT_PASSWORD_PROPERTY).toCharArray());
346 Session jcrSession = this.repository.login(credentials);
347
348 try {
349 Workspace workspace = jcrSession.getWorkspace();
350
351
352 if (xmlStream == null) {
353 throw new MissingNodetypesException();
354 }
355
356
357
358 Object[] types;
359
360 try {
361 types = (Object[]) NodeTypeReader.class.getMethod("read", new Class[]{InputStream.class}).invoke(null, xmlStream);
362 } catch (Exception e) {
363 throw new RepositoryException(e.getMessage(), e);
364 } finally {
365 IOUtils.closeQuietly(xmlStream);
366 }
367
368 NodeTypeManager ntMgr = workspace.getNodeTypeManager();
369 NodeTypeRegistry ntReg;
370 try {
371 ntReg = ((NodeTypeManagerImpl) ntMgr).getNodeTypeRegistry();
372 } catch (ClassCastException e) {
373
374
375 log.debug("Failed to get NodeTypeRegistry: ", e);
376 return;
377 }
378
379 for (int j = 0; j < types.length; j++) {
380 Object def = types[j];
381
382 Name ntname;
383 try {
384 ntname = (Name) PropertyUtils.getProperty(def, "name");
385 } catch (Exception e) {
386 throw new RepositoryException(e.getMessage(), e);
387 }
388
389 try {
390
391
392
393
394 Method method = ntReg.getClass().getMethod("getNodeTypeDef", Name.class);
395 method.invoke(ntReg, ntname);
396 } catch (IllegalArgumentException e) {
397 throw new RepositoryException(e.getMessage(), e);
398 } catch (IllegalAccessException e) {
399 throw new RepositoryException(e.getMessage(), e);
400 } catch (SecurityException e) {
401 throw new RepositoryException(e.getMessage(), e);
402 } catch (NoSuchMethodException e) {
403 throw new RepositoryException(e.getMessage(), e);
404 } catch (InvocationTargetException ite) {
405 if (ite.getTargetException() instanceof NoSuchNodeTypeException) {
406 log.info("Registering nodetype {} on repository {}", ntname, repositoryMapping.getName());
407
408 try {
409
410 getMethod(NodeTypeRegistry.class, "registerNodeType").invoke(ntReg, def);
411 } catch (Exception e) {
412 throw new RepositoryException(e.getMessage(), e);
413 }
414 }
415 }
416 }
417 } finally {
418 jcrSession.logout();
419 }
420 }
421
422 private Method getMethod(Class theclass, String methodName) throws NoSuchMethodException {
423 Method[] declaredMethods = theclass.getDeclaredMethods();
424
425 for (Method method : declaredMethods) {
426 if (method.getName().equals(methodName)) {
427 return method;
428 }
429 }
430
431 throw new NoSuchMethodException(theclass.getName() + "." + methodName + "()");
432 }
433
434
435
436
437 private InputStream getNodeTypeDefinition(String configuration) {
438
439 InputStream xml;
440
441 if (StringUtils.isNotEmpty(configuration)) {
442
443
444 xml = getClass().getResourceAsStream(configuration);
445 if (xml != null) {
446 log.info("Custom node types registered using {}", configuration);
447 return xml;
448 }
449
450
451 File nodeTypeDefinition = new File(fileSystemHelper.getAbsoluteFileSystemPath(configuration));
452 if (nodeTypeDefinition.exists()) {
453 try {
454 return new FileInputStream(nodeTypeDefinition);
455 } catch (FileNotFoundException e) {
456
457 log.error("File not found: {}", configuration);
458 }
459 }
460
461
462 log.error("Unable to find node type definition: {} for repository {}", configuration, this.repositoryMapping.getName());
463 }
464
465
466 xml = getClass().getResourceAsStream(MGNL_NODETYPES);
467
468 return xml;
469 }
470
471
472
473
474
475
476
477
478 protected void checkXmlSettings() {
479 if (SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_5)
480 && "org.apache.xalan.processor.TransformerFactoryImpl".equals(System
481 .getProperty("javax.xml.transform.TransformerFactory"))) {
482
483 String transformerClass = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";
484
485 try {
486 Class.forName(transformerClass);
487
488 System.setProperty("javax.xml.transform.TransformerFactory", transformerClass);
489
490 log.info("Java 1.5 detected, setting system property \"javax.xml.transform.TransformerFactory\" to \"{}\"", transformerClass);
491 } catch (Throwable e) {
492
493 }
494 }
495 }
496
497
498
499
500 private void validateWorkspaces() throws RepositoryException {
501 Iterator<String> configuredNames = repositoryMapping.getWorkspaces().iterator();
502 while (configuredNames.hasNext()) {
503 registerWorkspace(configuredNames.next());
504 }
505 }
506
507
508
509
510 @Override
511 public boolean registerWorkspace(String workspaceName) throws RepositoryException {
512
513 SimpleCredentials credentials = new SimpleCredentials(
514 configurationProperties.getProperty(MagnoliaConfigurationProperties.MAGNOLIA_JACKRABBIT_USER_PROPERTY),
515 configurationProperties.getProperty(MagnoliaConfigurationProperties.MAGNOLIA_JACKRABBIT_PASSWORD_PROPERTY).toCharArray());
516 Session jcrSession = this.repository.login(credentials);
517
518 try {
519 WorkspaceImpl defaultWorkspace = (WorkspaceImpl) jcrSession.getWorkspace();
520 String[] workspaceNames = defaultWorkspace.getAccessibleWorkspaceNames();
521
522 boolean alreadyExists = ArrayUtils.contains(workspaceNames, workspaceName);
523 if (!alreadyExists) {
524 defaultWorkspace.createWorkspace(workspaceName);
525 }
526 jcrSession.logout();
527
528 return !alreadyExists;
529 } catch (ClassCastException e) {
530
531
532 log.debug("Unable to register workspace, will continue", e);
533 } catch (Throwable t) {
534 log.error("Unable to register workspace, will continue", t);
535 }
536 return false;
537 }
538
539 @Override
540 public Session getSystemSession(String workspaceName) throws RepositoryException {
541 String user = configurationProperties.getProperty(MagnoliaConfigurationProperties.MAGNOLIA_JACKRABBIT_USER_PROPERTY);
542 String pwd = configurationProperties.getProperty(MagnoliaConfigurationProperties.MAGNOLIA_JACKRABBIT_PASSWORD_PROPERTY);
543 return this.repository.login(new SimpleCredentials(user, pwd.toCharArray()), workspaceName);
544 }
545
546 }