]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java
Move Eclipse components which are JCR specific.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / kernel / CmsDeployment.java
1 package org.argeo.cms.internal.kernel;
2
3 import static org.argeo.api.DataModelNamespace.CMS_DATA_MODEL_NAMESPACE;
4 import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX;
5
6 import java.io.File;
7 import java.io.IOException;
8 import java.io.InputStreamReader;
9 import java.io.Reader;
10 import java.lang.management.ManagementFactory;
11 import java.net.URL;
12 import java.nio.file.Files;
13 import java.nio.file.Path;
14 import java.nio.file.Paths;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Dictionary;
19 import java.util.HashSet;
20 import java.util.Hashtable;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 import javax.jcr.Repository;
27 import javax.jcr.RepositoryException;
28 import javax.jcr.Session;
29 import javax.security.auth.callback.CallbackHandler;
30 import javax.servlet.Servlet;
31 import javax.transaction.UserTransaction;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.jackrabbit.commons.cnd.CndImporter;
36 import org.apache.jackrabbit.core.RepositoryContext;
37 import org.apache.jackrabbit.core.RepositoryImpl;
38 import org.argeo.api.DataModelNamespace;
39 import org.argeo.api.NodeConstants;
40 import org.argeo.api.NodeDeployment;
41 import org.argeo.api.NodeState;
42 import org.argeo.api.NodeUtils;
43 import org.argeo.api.security.CryptoKeyring;
44 import org.argeo.api.security.Keyring;
45 import org.argeo.cms.ArgeoNames;
46 import org.argeo.cms.internal.http.CmsRemotingServlet;
47 import org.argeo.cms.internal.http.CmsWebDavServlet;
48 import org.argeo.cms.internal.http.HttpUtils;
49 import org.argeo.cms.internal.jcr.JcrInitUtils;
50 import org.argeo.jcr.Jcr;
51 import org.argeo.jcr.JcrException;
52 import org.argeo.jcr.JcrUtils;
53 import org.argeo.maintenance.backup.LogicalRestore;
54 import org.argeo.naming.LdapAttrs;
55 import org.argeo.osgi.useradmin.UserAdminConf;
56 import org.argeo.util.LangUtils;
57 import org.eclipse.equinox.http.jetty.JettyConfigurator;
58 import org.osgi.framework.Bundle;
59 import org.osgi.framework.BundleContext;
60 import org.osgi.framework.Constants;
61 import org.osgi.framework.FrameworkUtil;
62 import org.osgi.framework.InvalidSyntaxException;
63 import org.osgi.framework.ServiceReference;
64 import org.osgi.framework.wiring.BundleCapability;
65 import org.osgi.framework.wiring.BundleWire;
66 import org.osgi.framework.wiring.BundleWiring;
67 import org.osgi.service.cm.Configuration;
68 import org.osgi.service.cm.ConfigurationAdmin;
69 import org.osgi.service.cm.ManagedService;
70 import org.osgi.service.http.HttpService;
71 import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
72 import org.osgi.service.useradmin.Group;
73 import org.osgi.service.useradmin.Role;
74 import org.osgi.service.useradmin.UserAdmin;
75 import org.osgi.util.tracker.ServiceTracker;
76
77 /** Implementation of a CMS deployment. */
78 public class CmsDeployment implements NodeDeployment {
79 private final Log log = LogFactory.getLog(getClass());
80 private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
81
82 private DataModels dataModels;
83 private DeployConfig deployConfig;
84
85 private Long availableSince;
86
87 // private final boolean cleanState;
88
89 // private NodeHttp nodeHttp;
90 private String webDavConfig = HttpUtils.WEBDAV_CONFIG;
91
92 private boolean argeoDataModelExtensionsAvailable = false;
93
94 // Readiness
95 private boolean nodeAvailable = false;
96 private boolean userAdminAvailable = false;
97 private boolean httpExpected = false;
98 private boolean httpAvailable = false;
99
100 public CmsDeployment() {
101 // ServiceReference<NodeState> nodeStateSr = bc.getServiceReference(NodeState.class);
102 // if (nodeStateSr == null)
103 // throw new CmsException("No node state available");
104
105 // NodeState nodeState = bc.getService(nodeStateSr);
106 // cleanState = nodeState.isClean();
107
108 // nodeHttp = new NodeHttp();
109 dataModels = new DataModels(bc);
110 initTrackers();
111 }
112
113 private void initTrackers() {
114 ServiceTracker<?, ?> httpSt = new ServiceTracker<HttpService, HttpService>(bc, HttpService.class, null) {
115
116 @Override
117 public HttpService addingService(ServiceReference<HttpService> sr) {
118 httpAvailable = true;
119 Object httpPort = sr.getProperty("http.port");
120 Object httpsPort = sr.getProperty("https.port");
121 log.info(httpPortsMsg(httpPort, httpsPort));
122 checkReadiness();
123 return super.addingService(sr);
124 }
125 };
126 // httpSt.open();
127 KernelUtils.asyncOpen(httpSt);
128
129 ServiceTracker<?, ?> repoContextSt = new RepositoryContextStc();
130 // repoContextSt.open();
131 KernelUtils.asyncOpen(repoContextSt);
132
133 ServiceTracker<?, ?> userAdminSt = new ServiceTracker<UserAdmin, UserAdmin>(bc, UserAdmin.class, null) {
134 @Override
135 public UserAdmin addingService(ServiceReference<UserAdmin> reference) {
136 UserAdmin userAdmin = super.addingService(reference);
137 addStandardSystemRoles(userAdmin);
138 userAdminAvailable = true;
139 checkReadiness();
140 return userAdmin;
141 }
142 };
143 // userAdminSt.open();
144 KernelUtils.asyncOpen(userAdminSt);
145
146 ServiceTracker<?, ?> confAdminSt = new ServiceTracker<ConfigurationAdmin, ConfigurationAdmin>(bc,
147 ConfigurationAdmin.class, null) {
148 @Override
149 public ConfigurationAdmin addingService(ServiceReference<ConfigurationAdmin> reference) {
150 ConfigurationAdmin configurationAdmin = bc.getService(reference);
151 boolean isClean;
152 try {
153 Configuration[] confs = configurationAdmin
154 .listConfigurations("(service.factoryPid=" + NodeConstants.NODE_USER_ADMIN_PID + ")");
155 isClean = confs == null || confs.length == 0;
156 } catch (Exception e) {
157 throw new IllegalStateException("Cannot analyse clean state", e);
158 }
159 deployConfig = new DeployConfig(configurationAdmin, dataModels, isClean);
160 JcrInitUtils.addToDeployment(CmsDeployment.this);
161 httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null;
162 try {
163 Configuration[] configs = configurationAdmin
164 .listConfigurations("(service.factoryPid=" + NodeConstants.NODE_USER_ADMIN_PID + ")");
165
166 boolean hasDomain = false;
167 for (Configuration config : configs) {
168 Object realm = config.getProperties().get(UserAdminConf.realm.name());
169 if (realm != null) {
170 log.debug("Found realm: " + realm);
171 hasDomain = true;
172 }
173 }
174 if (hasDomain) {
175 loadIpaJaasConfiguration();
176 }
177 } catch (Exception e) {
178 throw new IllegalStateException("Cannot initialize config", e);
179 }
180 return super.addingService(reference);
181 }
182 };
183 // confAdminSt.open();
184 KernelUtils.asyncOpen(confAdminSt);
185 }
186
187 public void addFactoryDeployConfig(String factoryPid, Dictionary<String, Object> props) {
188 deployConfig.putFactoryDeployConfig(factoryPid, props);
189 deployConfig.save();
190 try {
191 deployConfig.loadConfigs();
192 } catch (IOException e) {
193 throw new IllegalStateException(e);
194 }
195 }
196
197 public Dictionary<String, Object> getProps(String factoryPid, String cn) {
198 return deployConfig.getProps(factoryPid, cn);
199 }
200
201 private String httpPortsMsg(Object httpPort, Object httpsPort) {
202 return (httpPort != null ? "HTTP " + httpPort + " " : " ") + (httpsPort != null ? "HTTPS " + httpsPort : "");
203 }
204
205 private void addStandardSystemRoles(UserAdmin userAdmin) {
206 // we assume UserTransaction is already available (TODO make it more robust)
207 UserTransaction userTransaction = bc.getService(bc.getServiceReference(UserTransaction.class));
208 try {
209 userTransaction.begin();
210 Role adminRole = userAdmin.getRole(NodeConstants.ROLE_ADMIN);
211 if (adminRole == null) {
212 adminRole = userAdmin.createRole(NodeConstants.ROLE_ADMIN, Role.GROUP);
213 }
214 if (userAdmin.getRole(NodeConstants.ROLE_USER_ADMIN) == null) {
215 Group userAdminRole = (Group) userAdmin.createRole(NodeConstants.ROLE_USER_ADMIN, Role.GROUP);
216 userAdminRole.addMember(adminRole);
217 }
218 userTransaction.commit();
219 } catch (Exception e) {
220 try {
221 userTransaction.rollback();
222 } catch (Exception e1) {
223 // silent
224 }
225 throw new IllegalStateException("Cannot add standard system roles", e);
226 }
227 }
228
229 private void loadIpaJaasConfiguration() {
230 if (System.getProperty(KernelConstants.JAAS_CONFIG_PROP) == null) {
231 String jaasConfig = KernelConstants.JAAS_CONFIG_IPA;
232 URL url = getClass().getClassLoader().getResource(jaasConfig);
233 KernelUtils.setJaasConfiguration(url);
234 log.debug("Set IPA JAAS configuration.");
235 }
236 }
237
238 public void shutdown() {
239 // if (nodeHttp != null)
240 // nodeHttp.destroy();
241
242 try {
243 for (ServiceReference<JackrabbitLocalRepository> sr : bc
244 .getServiceReferences(JackrabbitLocalRepository.class, null)) {
245 bc.getService(sr).destroy();
246 }
247 } catch (InvalidSyntaxException e1) {
248 log.error("Cannot sclean repsoitories", e1);
249 }
250
251 try {
252 JettyConfigurator.stopServer(KernelConstants.DEFAULT_JETTY_SERVER);
253 } catch (Exception e) {
254 log.error("Cannot stop default Jetty server.", e);
255 }
256
257 if (deployConfig != null) {
258 new Thread(() -> deployConfig.save(), "Save Argeo Deploy Config").start();
259 }
260 }
261
262 /**
263 * Checks whether the deployment is available according to expectations, and
264 * mark it as available.
265 */
266 private synchronized void checkReadiness() {
267 if (isAvailable())
268 return;
269 if (nodeAvailable && userAdminAvailable && (httpExpected ? httpAvailable : true)) {
270 String data = KernelUtils.getFrameworkProp(KernelUtils.OSGI_INSTANCE_AREA);
271 String state = KernelUtils.getFrameworkProp(KernelUtils.OSGI_CONFIGURATION_AREA);
272 availableSince = System.currentTimeMillis();
273 long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
274 String jvmUptimeStr = " in " + (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s";
275 log.info("## ARGEO NODE AVAILABLE" + (log.isDebugEnabled() ? jvmUptimeStr : "") + " ##");
276 if (log.isDebugEnabled()) {
277 log.debug("## state: " + state);
278 if (data != null)
279 log.debug("## data: " + data);
280 }
281 long begin = bc.getService(bc.getServiceReference(NodeState.class)).getAvailableSince();
282 long initDuration = System.currentTimeMillis() - begin;
283 if (log.isTraceEnabled())
284 log.trace("Kernel initialization took " + initDuration + "ms");
285 tributeToFreeSoftware(initDuration);
286 }
287 }
288
289 final private void tributeToFreeSoftware(long initDuration) {
290 if (log.isTraceEnabled()) {
291 long ms = initDuration / 100;
292 log.trace("Spend " + ms + "ms" + " reflecting on the progress brought to mankind" + " by Free Software...");
293 long beginNano = System.nanoTime();
294 try {
295 Thread.sleep(ms, 0);
296 } catch (InterruptedException e) {
297 // silent
298 }
299 long durationNano = System.nanoTime() - beginNano;
300 final double M = 1000d * 1000d;
301 double sleepAccuracy = ((double) durationNano) / (ms * M);
302 log.trace("Sleep accuracy: " + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100)) + " %");
303 }
304 }
305
306 private void prepareNodeRepository(Repository deployedNodeRepository, List<String> publishAsLocalRepo) {
307 if (availableSince != null) {
308 throw new IllegalStateException("Deployment is already available");
309 }
310
311 // home
312 prepareDataModel(NodeConstants.NODE_REPOSITORY, deployedNodeRepository, publishAsLocalRepo);
313
314 // init from backup
315 if (deployConfig.isFirstInit()) {
316 Path restorePath = Paths.get(System.getProperty("user.dir"), "restore");
317 if (Files.exists(restorePath)) {
318 if (log.isDebugEnabled())
319 log.debug("Found backup " + restorePath + ", restoring it...");
320 LogicalRestore logicalRestore = new LogicalRestore(bc, deployedNodeRepository, restorePath);
321 KernelUtils.doAsDataAdmin(logicalRestore);
322 log.info("Restored backup from " + restorePath);
323 }
324 }
325
326 // init from repository
327 Collection<ServiceReference<Repository>> initRepositorySr;
328 try {
329 initRepositorySr = bc.getServiceReferences(Repository.class,
330 "(" + NodeConstants.CN + "=" + NodeConstants.NODE_INIT + ")");
331 } catch (InvalidSyntaxException e1) {
332 throw new IllegalArgumentException(e1);
333 }
334 Iterator<ServiceReference<Repository>> it = initRepositorySr.iterator();
335 while (it.hasNext()) {
336 ServiceReference<Repository> sr = it.next();
337 Object labeledUri = sr.getProperties().get(LdapAttrs.labeledURI.name());
338 Repository initRepository = bc.getService(sr);
339 if (log.isDebugEnabled())
340 log.debug("Found init repository " + labeledUri + ", copying it...");
341 initFromRepository(deployedNodeRepository, initRepository);
342 log.info("Node repository initialised from " + labeledUri);
343 }
344 }
345
346 /** Init from a (typically remote) repository. */
347 private void initFromRepository(Repository deployedNodeRepository, Repository initRepository) {
348 Session initSession = null;
349 try {
350 initSession = initRepository.login();
351 workspaces: for (String workspaceName : initSession.getWorkspace().getAccessibleWorkspaceNames()) {
352 if ("security".equals(workspaceName))
353 continue workspaces;
354 if (log.isDebugEnabled())
355 log.debug("Copying workspace " + workspaceName + " from init repository...");
356 long begin = System.currentTimeMillis();
357 Session targetSession = null;
358 Session sourceSession = null;
359 try {
360 try {
361 targetSession = NodeUtils.openDataAdminSession(deployedNodeRepository, workspaceName);
362 } catch (IllegalArgumentException e) {// no such workspace
363 Session adminSession = NodeUtils.openDataAdminSession(deployedNodeRepository, null);
364 try {
365 adminSession.getWorkspace().createWorkspace(workspaceName);
366 } finally {
367 Jcr.logout(adminSession);
368 }
369 targetSession = NodeUtils.openDataAdminSession(deployedNodeRepository, workspaceName);
370 }
371 sourceSession = initRepository.login(workspaceName);
372 // JcrUtils.copyWorkspaceXml(sourceSession, targetSession);
373 // TODO deal with referenceable nodes
374 JcrUtils.copy(sourceSession.getRootNode(), targetSession.getRootNode());
375 targetSession.save();
376 long duration = System.currentTimeMillis() - begin;
377 if (log.isDebugEnabled())
378 log.debug("Copied workspace " + workspaceName + " from init repository in " + (duration / 1000)
379 + " s");
380 } catch (Exception e) {
381 log.error("Cannot copy workspace " + workspaceName + " from init repository.", e);
382 } finally {
383 Jcr.logout(sourceSession);
384 Jcr.logout(targetSession);
385 }
386 }
387 } catch (RepositoryException e) {
388 throw new JcrException(e);
389 } finally {
390 Jcr.logout(initSession);
391 }
392 }
393
394 private void prepareHomeRepository(RepositoryImpl deployedRepository) {
395 Session adminSession = KernelUtils.openAdminSession(deployedRepository);
396 try {
397 argeoDataModelExtensionsAvailable = Arrays
398 .asList(adminSession.getWorkspace().getNamespaceRegistry().getURIs())
399 .contains(ArgeoNames.ARGEO_NAMESPACE);
400 } catch (RepositoryException e) {
401 log.warn("Cannot check whether Argeo namespace is registered assuming it isn't.", e);
402 argeoDataModelExtensionsAvailable = false;
403 } finally {
404 JcrUtils.logoutQuietly(adminSession);
405 }
406
407 // Publish home with the highest service ranking
408 Hashtable<String, Object> regProps = new Hashtable<>();
409 regProps.put(NodeConstants.CN, NodeConstants.EGO_REPOSITORY);
410 regProps.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
411 Repository egoRepository = new EgoRepository(deployedRepository, false);
412 bc.registerService(Repository.class, egoRepository, regProps);
413 registerRepositoryServlets(NodeConstants.EGO_REPOSITORY, egoRepository);
414
415 // Keyring only if Argeo extensions are available
416 if (argeoDataModelExtensionsAvailable) {
417 new ServiceTracker<CallbackHandler, CallbackHandler>(bc, CallbackHandler.class, null) {
418
419 @Override
420 public CallbackHandler addingService(ServiceReference<CallbackHandler> reference) {
421 NodeKeyRing nodeKeyring = new NodeKeyRing(egoRepository);
422 CallbackHandler callbackHandler = bc.getService(reference);
423 nodeKeyring.setDefaultCallbackHandler(callbackHandler);
424 bc.registerService(LangUtils.names(Keyring.class, CryptoKeyring.class, ManagedService.class),
425 nodeKeyring, LangUtils.dict(Constants.SERVICE_PID, NodeConstants.NODE_KEYRING_PID));
426 return callbackHandler;
427 }
428
429 }.open();
430 }
431 }
432
433 /** Session is logged out. */
434 private void prepareDataModel(String cn, Repository repository, List<String> publishAsLocalRepo) {
435 Session adminSession = KernelUtils.openAdminSession(repository);
436 try {
437 Set<String> processed = new HashSet<String>();
438 bundles: for (Bundle bundle : bc.getBundles()) {
439 BundleWiring wiring = bundle.adapt(BundleWiring.class);
440 if (wiring == null)
441 continue bundles;
442 if (NodeConstants.NODE_REPOSITORY.equals(cn))// process all data models
443 processWiring(cn, adminSession, wiring, processed, false, publishAsLocalRepo);
444 else {
445 List<BundleCapability> capabilities = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
446 for (BundleCapability capability : capabilities) {
447 String dataModelName = (String) capability.getAttributes().get(DataModelNamespace.NAME);
448 if (dataModelName.equals(cn))// process only own data model
449 processWiring(cn, adminSession, wiring, processed, false, publishAsLocalRepo);
450 }
451 }
452 }
453 } finally {
454 JcrUtils.logoutQuietly(adminSession);
455 }
456 }
457
458 private void processWiring(String cn, Session adminSession, BundleWiring wiring, Set<String> processed,
459 boolean importListedAbstractModels, List<String> publishAsLocalRepo) {
460 // recursively process requirements first
461 List<BundleWire> requiredWires = wiring.getRequiredWires(CMS_DATA_MODEL_NAMESPACE);
462 for (BundleWire wire : requiredWires) {
463 processWiring(cn, adminSession, wire.getProviderWiring(), processed, true, publishAsLocalRepo);
464 }
465
466 List<BundleCapability> capabilities = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
467 capabilities: for (BundleCapability capability : capabilities) {
468 if (!importListedAbstractModels
469 && KernelUtils.asBoolean((String) capability.getAttributes().get(DataModelNamespace.ABSTRACT))) {
470 continue capabilities;
471 }
472 boolean publish = registerDataModelCapability(cn, adminSession, capability, processed);
473 if (publish)
474 publishAsLocalRepo.add((String) capability.getAttributes().get(DataModelNamespace.NAME));
475 }
476 }
477
478 private boolean registerDataModelCapability(String cn, Session adminSession, BundleCapability capability,
479 Set<String> processed) {
480 Map<String, Object> attrs = capability.getAttributes();
481 String name = (String) attrs.get(DataModelNamespace.NAME);
482 if (processed.contains(name)) {
483 if (log.isTraceEnabled())
484 log.trace("Data model " + name + " has already been processed");
485 return false;
486 }
487
488 // CND
489 String path = (String) attrs.get(DataModelNamespace.CND);
490 if (path != null) {
491 File dataModel = bc.getBundle().getDataFile("dataModels/" + path);
492 if (!dataModel.exists()) {
493 URL url = capability.getRevision().getBundle().getResource(path);
494 if (url == null)
495 throw new IllegalArgumentException("No data model '" + name + "' found under path " + path);
496 try (Reader reader = new InputStreamReader(url.openStream())) {
497 CndImporter.registerNodeTypes(reader, adminSession, true);
498 processed.add(name);
499 dataModel.getParentFile().mkdirs();
500 dataModel.createNewFile();
501 if (log.isDebugEnabled())
502 log.debug("Registered CND " + url);
503 } catch (Exception e) {
504 log.error("Cannot import CND " + url, e);
505 }
506 }
507 }
508
509 if (KernelUtils.asBoolean((String) attrs.get(DataModelNamespace.ABSTRACT)))
510 return false;
511 // Non abstract
512 boolean isStandalone = deployConfig.isStandalone(name);
513 boolean publishLocalRepo;
514 if (isStandalone && name.equals(cn))// includes the node itself
515 publishLocalRepo = true;
516 else if (!isStandalone && cn.equals(NodeConstants.NODE_REPOSITORY))
517 publishLocalRepo = true;
518 else
519 publishLocalRepo = false;
520
521 return publishLocalRepo;
522 }
523
524 private void publishLocalRepo(String dataModelName, Repository repository) {
525 Hashtable<String, Object> properties = new Hashtable<>();
526 properties.put(NodeConstants.CN, dataModelName);
527 LocalRepository localRepository;
528 String[] classes;
529 if (repository instanceof RepositoryImpl) {
530 localRepository = new JackrabbitLocalRepository((RepositoryImpl) repository, dataModelName);
531 classes = new String[] { Repository.class.getName(), LocalRepository.class.getName(),
532 JackrabbitLocalRepository.class.getName() };
533 } else {
534 localRepository = new LocalRepository(repository, dataModelName);
535 classes = new String[] { Repository.class.getName(), LocalRepository.class.getName() };
536 }
537 bc.registerService(classes, localRepository, properties);
538
539 // TODO make it configurable
540 registerRepositoryServlets(dataModelName, localRepository);
541 if (log.isTraceEnabled())
542 log.trace("Published data model " + dataModelName);
543 }
544
545 @Override
546 public synchronized Long getAvailableSince() {
547 return availableSince;
548 }
549
550 public synchronized boolean isAvailable() {
551 return availableSince != null;
552 }
553
554 protected void registerRepositoryServlets(String alias, Repository repository) {
555 registerRemotingServlet(alias, repository);
556 registerWebdavServlet(alias, repository);
557 }
558
559 protected void registerWebdavServlet(String alias, Repository repository) {
560 CmsWebDavServlet webdavServlet = new CmsWebDavServlet(alias, repository);
561 Hashtable<String, String> ip = new Hashtable<>();
562 ip.put(HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX + CmsWebDavServlet.INIT_PARAM_RESOURCE_CONFIG, webDavConfig);
563 ip.put(HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX + CmsWebDavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX,
564 "/" + alias);
565
566 ip.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/" + alias + "/*");
567 ip.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
568 "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH + "=" + NodeConstants.PATH_DATA + ")");
569 bc.registerService(Servlet.class, webdavServlet, ip);
570 }
571
572 protected void registerRemotingServlet(String alias, Repository repository) {
573 CmsRemotingServlet remotingServlet = new CmsRemotingServlet(alias, repository);
574 Hashtable<String, String> ip = new Hashtable<>();
575 ip.put(NodeConstants.CN, alias);
576 // Properties ip = new Properties();
577 ip.put(HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX + CmsRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX,
578 "/" + alias);
579 ip.put(HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX + CmsRemotingServlet.INIT_PARAM_AUTHENTICATE_HEADER,
580 "Negotiate");
581
582 // Looks like a bug in Jackrabbit remoting init
583 Path tmpDir;
584 try {
585 tmpDir = Files.createTempDirectory("remoting_" + alias);
586 } catch (IOException e) {
587 throw new RuntimeException("Cannot create temp directory for remoting servlet", e);
588 }
589 ip.put(HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX + CmsRemotingServlet.INIT_PARAM_HOME, tmpDir.toString());
590 ip.put(HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX + CmsRemotingServlet.INIT_PARAM_TMP_DIRECTORY,
591 "remoting_" + alias);
592 ip.put(HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX + CmsRemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG,
593 HttpUtils.DEFAULT_PROTECTED_HANDLERS);
594 ip.put(HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX + CmsRemotingServlet.INIT_PARAM_CREATE_ABSOLUTE_URI, "false");
595
596 ip.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/" + alias + "/*");
597 ip.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
598 "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH + "=" + NodeConstants.PATH_JCR + ")");
599 bc.registerService(Servlet.class, remotingServlet, ip);
600 }
601
602 private class RepositoryContextStc extends ServiceTracker<RepositoryContext, RepositoryContext> {
603
604 public RepositoryContextStc() {
605 super(bc, RepositoryContext.class, null);
606 }
607
608 @Override
609 public RepositoryContext addingService(ServiceReference<RepositoryContext> reference) {
610 RepositoryContext repoContext = bc.getService(reference);
611 String cn = (String) reference.getProperty(NodeConstants.CN);
612 if (cn != null) {
613 List<String> publishAsLocalRepo = new ArrayList<>();
614 if (cn.equals(NodeConstants.NODE_REPOSITORY)) {
615 // JackrabbitDataModelMigration.clearRepositoryCaches(repoContext.getRepositoryConfig());
616 prepareNodeRepository(repoContext.getRepository(), publishAsLocalRepo);
617 // TODO separate home repository
618 prepareHomeRepository(repoContext.getRepository());
619 registerRepositoryServlets(cn, repoContext.getRepository());
620 nodeAvailable = true;
621 checkReadiness();
622 } else {
623 prepareDataModel(cn, repoContext.getRepository(), publishAsLocalRepo);
624 }
625 // Publish all at once, so that bundles with multiple CNDs are consistent
626 for (String dataModelName : publishAsLocalRepo)
627 publishLocalRepo(dataModelName, repoContext.getRepository());
628 }
629 return repoContext;
630 }
631
632 @Override
633 public void modifiedService(ServiceReference<RepositoryContext> reference, RepositoryContext service) {
634 }
635
636 @Override
637 public void removedService(ServiceReference<RepositoryContext> reference, RepositoryContext service) {
638 }
639
640 }
641
642 }