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