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