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