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