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