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