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