From: Mathieu Baudier Date: Sun, 4 Feb 2018 14:07:06 +0000 (+0100) Subject: Make CMS startup more asynchronous X-Git-Tag: argeo-commons-2.1.71~21 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=6a62c05a487ba34946b1924a039603e68b1d54e6;p=lgpl%2Fargeo-commons.git Make CMS startup more asynchronous --- diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/CmsSessionProvider.java b/org.argeo.cms/src/org/argeo/cms/internal/http/CmsSessionProvider.java index 4f0c184db..943decaf8 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/CmsSessionProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/CmsSessionProvider.java @@ -19,7 +19,7 @@ import org.argeo.jcr.JcrUtils; * Implements an open session in view patter: a new JCR session is created for * each request */ -class CmsSessionProvider implements SessionProvider, Serializable { +public class CmsSessionProvider implements SessionProvider, Serializable { private static final long serialVersionUID = -1358136599534938466L; private final static Log log = LogFactory.getLog(CmsSessionProvider.class); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java b/org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java index b7a8d0f9e..93f63530e 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java @@ -16,7 +16,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.service.http.HttpContext; -class DataHttpContext implements HttpContext { +public class DataHttpContext implements HttpContext { private final static Log log = LogFactory.getLog(DataHttpContext.class); private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/HttpUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/http/HttpUtils.java index efa2d661a..58d9324ec 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/HttpUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/HttpUtils.java @@ -6,12 +6,12 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; -class HttpUtils { - final static String HEADER_AUTHORIZATION = "Authorization"; - final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; +public class HttpUtils { + public final static String HEADER_AUTHORIZATION = "Authorization"; + public final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; - final static String DEFAULT_PROTECTED_HANDLERS = "/org/argeo/cms/internal/http/protectedHandlers.xml"; - final static String WEBDAV_CONFIG = "/org/argeo/cms/internal/http/webdav-config.xml"; + public final static String DEFAULT_PROTECTED_HANDLERS = "/org/argeo/cms/internal/http/protectedHandlers.xml"; + public final static String WEBDAV_CONFIG = "/org/argeo/cms/internal/http/webdav-config.xml"; static boolean isBrowser(String userAgent) { return userAgent.contains("webkit") || userAgent.contains("gecko") || userAgent.contains("firefox") @@ -19,7 +19,7 @@ class HttpUtils { || userAgent.contains("opera") || userAgent.contains("browser"); } - static void logRequestHeaders(Log log, HttpServletRequest request) { + public static void logRequestHeaders(Log log, HttpServletRequest request) { if (!log.isDebugEnabled()) return; for (Enumeration headerNames = request.getHeaderNames(); headerNames.hasMoreElements();) { @@ -30,7 +30,7 @@ class HttpUtils { log.debug(request.getRequestURI() + "\n"); } - static void logRequest(Log log,HttpServletRequest request) { + public static void logRequest(Log log,HttpServletRequest request) { log.debug("contextPath=" + request.getContextPath()); log.debug("servletPath=" + request.getServletPath()); log.debug("requestURI=" + request.getRequestURI()); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java b/org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java index d77e39cb9..34bdcaa17 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java @@ -34,7 +34,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; -class LinkServlet extends HttpServlet { +public class LinkServlet extends HttpServlet { private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); private static final long serialVersionUID = 3749990143146845708L; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/NodeHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/http/NodeHttp.java deleted file mode 100644 index 4cc0b5538..000000000 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/NodeHttp.java +++ /dev/null @@ -1,329 +0,0 @@ -package org.argeo.cms.internal.http; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Properties; - -import javax.jcr.Repository; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.jackrabbit.server.SessionProvider; -import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet; -import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet; -import org.argeo.cms.CmsException; -import org.argeo.cms.internal.kernel.KernelConstants; -import org.argeo.node.NodeConstants; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; -import org.osgi.service.http.HttpService; -import org.osgi.service.http.NamespaceException; -import org.osgi.util.tracker.ServiceTracker; - -/** - * Intercepts and enriches http access, mainly focusing on security and - * transactionality. - */ -public class NodeHttp implements KernelConstants { - private final static Log log = LogFactory.getLog(NodeHttp.class); - - public final static String DEFAULT_SERVICE = "HTTP"; - - private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); - - private ServiceTracker repositories; - private final ServiceTracker httpServiceTracker; - - private String httpRealm = "Argeo"; - - public NodeHttp() { - httpServiceTracker = new PrepareHttpStc(); - httpServiceTracker.open(); - } - - public void destroy() { - if (repositories != null) - repositories.close(); - } - - void registerRepositoryServlets(HttpService httpService, String alias, Repository repository) { - if (httpService == null) - throw new CmsException("No HTTP service available"); - try { - registerWebdavServlet(httpService, alias, repository); - registerRemotingServlet(httpService, alias, repository); - if (NodeConstants.HOME.equals(alias)) - registerFilesServlet(httpService, alias, repository); - if (log.isDebugEnabled()) - log.debug("Registered servlets for repository '" + alias + "'"); - } catch (Exception e) { - throw new CmsException("Could not register servlets for repository '" + alias + "'", e); - } - } - - void unregisterRepositoryServlets(HttpService httpService, String alias) { - if (httpService == null) - return; - try { - httpService.unregister(webdavPath(alias)); - httpService.unregister(remotingPath(alias)); - if (NodeConstants.HOME.equals(alias)) - httpService.unregister(filesPath(alias)); - if (log.isDebugEnabled()) - log.debug("Unregistered servlets for repository '" + alias + "'"); - } catch (Exception e) { - log.error("Could not unregister servlets for repository '" + alias + "'", e); - } - } - - void registerWebdavServlet(HttpService httpService, String alias, Repository repository) - throws NamespaceException, ServletException { - // WebdavServlet webdavServlet = new WebdavServlet(repository, new - // OpenInViewSessionProvider(alias)); - WebdavServlet webdavServlet = new WebdavServlet(repository, new CmsSessionProvider(alias)); - String path = webdavPath(alias); - Properties ip = new Properties(); - ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, HttpUtils.WEBDAV_CONFIG); - ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path); - httpService.registerServlet(path, webdavServlet, ip, new DataHttpContext(httpRealm)); - } - - void registerFilesServlet(HttpService httpService, String alias, Repository repository) - throws NamespaceException, ServletException { - WebdavServlet filesServlet = new WebdavServlet(repository, new CmsSessionProvider(alias)); - String path = filesPath(alias); - Properties ip = new Properties(); - ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, HttpUtils.WEBDAV_CONFIG); - ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path); - httpService.registerServlet(path, filesServlet, ip, new PrivateHttpContext(httpRealm, true)); - } - - void registerRemotingServlet(HttpService httpService, String alias, Repository repository) - throws NamespaceException, ServletException { - RemotingServlet remotingServlet = new RemotingServlet(repository, new CmsSessionProvider(alias)); - String path = remotingPath(alias); - Properties ip = new Properties(); - ip.setProperty(JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path); - ip.setProperty(JcrRemotingServlet.INIT_PARAM_AUTHENTICATE_HEADER, "Negotiate"); - - // Looks like a bug in Jackrabbit remoting init - Path tmpDir; - try { - tmpDir = Files.createTempDirectory("remoting_" + alias); - } catch (IOException e) { - throw new CmsException("Cannot create temp directory for remoting servlet", e); - } - ip.setProperty(RemotingServlet.INIT_PARAM_HOME, tmpDir.toString()); - ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting_" + alias); - ip.setProperty(RemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG, HttpUtils.DEFAULT_PROTECTED_HANDLERS); - ip.setProperty(RemotingServlet.INIT_PARAM_CREATE_ABSOLUTE_URI, "false"); - httpService.registerServlet(path, remotingServlet, ip, new PrivateHttpContext(httpRealm)); - } - - private String webdavPath(String alias) { - return NodeConstants.PATH_DATA + "/" + alias; - } - - private String remotingPath(String alias) { - return NodeConstants.PATH_JCR + "/" + alias; - } - - private String filesPath(String alias) { - return NodeConstants.PATH_FILES; - } - - // private Subject subjectFromRequest(HttpServletRequest request, - // HttpServletResponse response) { - // Authorization authorization = (Authorization) - // request.getAttribute(HttpContext.AUTHORIZATION); - // if (authorization == null) - // throw new CmsException("Not authenticated"); - // try { - // LoginContext lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, - // new HttpRequestCallbackHandler(request, response)); - // lc.login(); - // return lc.getSubject(); - // } catch (LoginException e) { - // throw new CmsException("Cannot login", e); - // } - // } - - private class RepositoriesStc extends ServiceTracker { - private final HttpService httpService; - - public RepositoriesStc(HttpService httpService) { - super(bc, Repository.class, null); - this.httpService = httpService; - } - - @Override - public Repository addingService(ServiceReference reference) { - Repository repository = bc.getService(reference); - Object jcrRepoAlias = reference.getProperty(NodeConstants.CN); - if (jcrRepoAlias != null) { - String alias = jcrRepoAlias.toString(); - registerRepositoryServlets(httpService, alias, repository); - } - return repository; - } - - @Override - public void modifiedService(ServiceReference reference, Repository service) { - } - - @Override - public void removedService(ServiceReference reference, Repository service) { - Object jcrRepoAlias = reference.getProperty(NodeConstants.CN); - if (jcrRepoAlias != null) { - String alias = jcrRepoAlias.toString(); - unregisterRepositoryServlets(httpService, alias); - } - } - } - - private class PrepareHttpStc extends ServiceTracker { - // private DataHttp dataHttp; - // private NodeHttp nodeHttp; - - public PrepareHttpStc() { - super(bc, HttpService.class, null); - } - - @Override - public HttpService addingService(ServiceReference reference) { - HttpService httpService = addHttpService(reference); - return httpService; - } - - @Override - public void removedService(ServiceReference reference, HttpService service) { - // if (dataHttp != null) - // dataHttp.destroy(); - // dataHttp = null; - // if (nodeHttp != null) - // nodeHttp.destroy(); - // nodeHttp = null; - // destroy(); - repositories.close(); - repositories = null; - } - - private HttpService addHttpService(ServiceReference sr) { - HttpService httpService = bc.getService(sr); - // TODO find constants - Object httpPort = sr.getProperty("http.port"); - Object httpsPort = sr.getProperty("https.port"); - - try { - httpService.registerServlet("/!", new LinkServlet(), null, null); - httpService.registerServlet("/robots.txt", new RobotServlet(), null, null); - } catch (Exception e) { - throw new CmsException("Cannot register filters", e); - } - // track repositories - if (repositories != null) - throw new CmsException("An http service is already configured"); - repositories = new RepositoriesStc(httpService); - repositories.open(); - log.info(httpPortsMsg(httpPort, httpsPort)); - // httpAvailable = true; - // checkReadiness(); - - bc.registerService(NodeHttp.class, NodeHttp.this, null); - return httpService; - } - - private String httpPortsMsg(Object httpPort, Object httpsPort) { - return "HTTP " + httpPort + (httpsPort != null ? " - HTTPS " + httpsPort : ""); - } - } - - private class WebdavServlet extends SimpleWebdavServlet { - private static final long serialVersionUID = -4687354117811443881L; - private final Repository repository; - - public WebdavServlet(Repository repository, SessionProvider sessionProvider) { - this.repository = repository; - setSessionProvider(sessionProvider); - } - - public Repository getRepository() { - return repository; - } - - @Override - protected void service(final HttpServletRequest request, final HttpServletResponse response) - throws ServletException, IOException { - WebdavServlet.super.service(request, response); - // try { - // Subject subject = subjectFromRequest(request); - // // TODO make it stronger, with eTags. - // // if (CurrentUser.isAnonymous(subject) && - // // request.getMethod().equals("GET")) { - // // response.setHeader("Cache-Control", "no-transform, public, - // // max-age=300, s-maxage=900"); - // // } - // - // Subject.doAs(subject, new PrivilegedExceptionAction() { - // @Override - // public Void run() throws Exception { - // WebdavServlet.super.service(request, response); - // return null; - // } - // }); - // } catch (PrivilegedActionException e) { - // throw new CmsException("Cannot process webdav request", - // e.getException()); - // } - } - - } - - private class RemotingServlet extends JcrRemotingServlet { - private final Log log = LogFactory.getLog(RemotingServlet.class); - private static final long serialVersionUID = 4605238259548058883L; - private final Repository repository; - private final SessionProvider sessionProvider; - - public RemotingServlet(Repository repository, SessionProvider sessionProvider) { - this.repository = repository; - this.sessionProvider = sessionProvider; - } - - @Override - protected Repository getRepository() { - return repository; - } - - @Override - protected SessionProvider getSessionProvider() { - return sessionProvider; - } - - @Override - protected void service(final HttpServletRequest request, final HttpServletResponse response) - throws ServletException, IOException { - // try { - // Subject subject = subjectFromRequest(request, response); - // Subject.doAs(subject, new PrivilegedExceptionAction() { - // @Override - // public Void run() throws Exception { - if (log.isTraceEnabled()) - HttpUtils.logRequest(log, request); - RemotingServlet.super.service(request, response); - // return null; - // } - // }); - // } catch (PrivilegedActionException e) { - // throw new CmsException("Cannot process JCR remoting request", - // e.getException()); - // } - } - } - -} diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/PrivateHttpContext.java b/org.argeo.cms/src/org/argeo/cms/internal/http/PrivateHttpContext.java index 2babd188b..c3f2a1c80 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/PrivateHttpContext.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/PrivateHttpContext.java @@ -5,7 +5,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** Requests authorisation */ -class PrivateHttpContext extends DataHttpContext { +public class PrivateHttpContext extends DataHttpContext { public PrivateHttpContext(String httpAuthrealm, boolean forceBasic) { super(httpAuthrealm, forceBasic); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/RobotServlet.java b/org.argeo.cms/src/org/argeo/cms/internal/http/RobotServlet.java index 92d9eb78c..6d3d302b7 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/RobotServlet.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/RobotServlet.java @@ -8,7 +8,7 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -class RobotServlet extends HttpServlet { +public class RobotServlet extends HttpServlet { private static final long serialVersionUID = 7935661175336419089L; @Override diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java index 64dbaab74..7a8071f4a 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/client/SpnegoAuthScheme.java @@ -20,7 +20,7 @@ import org.apache.commons.httpclient.auth.MalformedChallengeException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.DefaultHttpParams; import org.apache.commons.httpclient.params.HttpParams; -import org.argeo.cms.internal.http.NodeHttp; +import org.argeo.cms.internal.kernel.NodeHttp; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSManager; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java index 25746a481..980dde975 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java @@ -59,6 +59,7 @@ public class Activator implements BundleActivator { initSecurity(); initArgeoLogger(); initNode(); + log.debug("Kernel bundle started"); } catch (Exception e) { log.error("## FATAL: CMS activator failed", e); } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java index 7dedfe811..7f547fd11 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java @@ -21,7 +21,6 @@ import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.commons.cnd.CndImporter; import org.apache.jackrabbit.core.RepositoryContext; import org.argeo.cms.CmsException; -import org.argeo.cms.internal.http.NodeHttp; import org.argeo.jcr.JcrUtils; import org.argeo.node.DataModelNamespace; import org.argeo.node.NodeConstants; @@ -78,7 +77,7 @@ public class CmsDeployment implements NodeDeployment { } private void initTrackers() { - new ServiceTracker(bc, NodeHttp.class, null) { + ServiceTracker httpSt = new ServiceTracker(bc, NodeHttp.class, null) { @Override public NodeHttp addingService(ServiceReference reference) { @@ -86,17 +85,27 @@ public class CmsDeployment implements NodeDeployment { checkReadiness(); return super.addingService(reference); } - }.open(); - new RepositoryContextStc().open(); - new ServiceTracker(bc, UserAdmin.class, null) { + }; +// httpSt.open(); + KernelUtils.asyncOpen(httpSt); + + ServiceTracker repoContextSt = new RepositoryContextStc(); +// repoContextSt.open(); + KernelUtils.asyncOpen(repoContextSt); + + ServiceTracker userAdminSt = new ServiceTracker(bc, UserAdmin.class, null) { @Override public UserAdmin addingService(ServiceReference reference) { userAdminAvailable = true; checkReadiness(); return super.addingService(reference); } - }.open(); - new ServiceTracker(bc, ConfigurationAdmin.class, null) { + }; +// userAdminSt.open(); + KernelUtils.asyncOpen(userAdminSt); + + ServiceTracker confAdminSt = new ServiceTracker(bc, + ConfigurationAdmin.class, null) { @Override public ConfigurationAdmin addingService(ServiceReference reference) { ConfigurationAdmin configurationAdmin = bc.getService(reference); @@ -130,7 +139,9 @@ public class CmsDeployment implements NodeDeployment { } return super.addingService(reference); } - }.open(); + }; +// confAdminSt.open(); + KernelUtils.asyncOpen(confAdminSt); } private void loadIpaJaasConfiguration() { diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java index b3fb33ac3..63cb356d3 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java @@ -29,6 +29,7 @@ import org.argeo.node.NodeConstants; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; +import org.osgi.util.tracker.ServiceTracker; /** Package utilities */ class KernelUtils implements KernelConstants { @@ -188,9 +189,20 @@ class KernelUtils implements KernelConstants { }); } + static void asyncOpen(ServiceTracker st) { + Runnable run = new Runnable() { + + @Override + public void run() { + st.open(); + } + }; + new Thread(run, "Open service tracker " + st).start(); + } + /** - * @return the {@link BundleContext} of the {@link Bundle} which provided - * this class, never null. + * @return the {@link BundleContext} of the {@link Bundle} which provided this + * class, never null. * @throws CmsException * if the related bundle is not active */ diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java new file mode 100644 index 000000000..f17c98241 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java @@ -0,0 +1,342 @@ +package org.argeo.cms.internal.kernel; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; + +import javax.jcr.Repository; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jackrabbit.server.SessionProvider; +import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet; +import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet; +import org.argeo.cms.CmsException; +import org.argeo.cms.internal.http.CmsSessionProvider; +import org.argeo.cms.internal.http.DataHttpContext; +import org.argeo.cms.internal.http.HttpUtils; +import org.argeo.cms.internal.http.LinkServlet; +import org.argeo.cms.internal.http.PrivateHttpContext; +import org.argeo.cms.internal.http.RobotServlet; +import org.argeo.node.NodeConstants; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.service.http.HttpService; +import org.osgi.service.http.NamespaceException; +import org.osgi.util.tracker.ServiceTracker; + +/** + * Intercepts and enriches http access, mainly focusing on security and + * transactionality. + */ +public class NodeHttp implements KernelConstants { + private final static Log log = LogFactory.getLog(NodeHttp.class); + + public final static String DEFAULT_SERVICE = "HTTP"; + + private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); + + private ServiceTracker repositories; + private final ServiceTracker httpServiceTracker; + + private static String httpRealm = "Argeo"; + + public NodeHttp() { + httpServiceTracker = new PrepareHttpStc(); + // httpServiceTracker.open(); + KernelUtils.asyncOpen(httpServiceTracker); + } + + public void destroy() { + if (repositories != null) + repositories.close(); + } + + public static void registerRepositoryServlets(HttpService httpService, String alias, Repository repository) { + if (httpService == null) + throw new CmsException("No HTTP service available"); + try { + registerWebdavServlet(httpService, alias, repository); + registerRemotingServlet(httpService, alias, repository); + if (NodeConstants.HOME.equals(alias)) + registerFilesServlet(httpService, alias, repository); + if (log.isDebugEnabled()) + log.debug("Registered servlets for repository '" + alias + "'"); + } catch (Exception e) { + throw new CmsException("Could not register servlets for repository '" + alias + "'", e); + } + } + + public static void unregisterRepositoryServlets(HttpService httpService, String alias) { + if (httpService == null) + return; + try { + httpService.unregister(webdavPath(alias)); + httpService.unregister(remotingPath(alias)); + if (NodeConstants.HOME.equals(alias)) + httpService.unregister(filesPath(alias)); + if (log.isDebugEnabled()) + log.debug("Unregistered servlets for repository '" + alias + "'"); + } catch (Exception e) { + log.error("Could not unregister servlets for repository '" + alias + "'", e); + } + } + + static void registerWebdavServlet(HttpService httpService, String alias, Repository repository) + throws NamespaceException, ServletException { + // WebdavServlet webdavServlet = new WebdavServlet(repository, new + // OpenInViewSessionProvider(alias)); + WebdavServlet webdavServlet = new WebdavServlet(repository, new CmsSessionProvider(alias)); + String path = webdavPath(alias); + Properties ip = new Properties(); + ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, HttpUtils.WEBDAV_CONFIG); + ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path); + httpService.registerServlet(path, webdavServlet, ip, new DataHttpContext(httpRealm)); + } + + static void registerFilesServlet(HttpService httpService, String alias, Repository repository) + throws NamespaceException, ServletException { + WebdavServlet filesServlet = new WebdavServlet(repository, new CmsSessionProvider(alias)); + String path = filesPath(alias); + Properties ip = new Properties(); + ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, HttpUtils.WEBDAV_CONFIG); + ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path); + httpService.registerServlet(path, filesServlet, ip, new PrivateHttpContext(httpRealm, true)); + } + + static void registerRemotingServlet(HttpService httpService, String alias, Repository repository) + throws NamespaceException, ServletException { + RemotingServlet remotingServlet = new RemotingServlet(repository, new CmsSessionProvider(alias)); + String path = remotingPath(alias); + Properties ip = new Properties(); + ip.setProperty(JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path); + ip.setProperty(JcrRemotingServlet.INIT_PARAM_AUTHENTICATE_HEADER, "Negotiate"); + + // Looks like a bug in Jackrabbit remoting init + Path tmpDir; + try { + tmpDir = Files.createTempDirectory("remoting_" + alias); + } catch (IOException e) { + throw new CmsException("Cannot create temp directory for remoting servlet", e); + } + ip.setProperty(RemotingServlet.INIT_PARAM_HOME, tmpDir.toString()); + ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting_" + alias); + ip.setProperty(RemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG, HttpUtils.DEFAULT_PROTECTED_HANDLERS); + ip.setProperty(RemotingServlet.INIT_PARAM_CREATE_ABSOLUTE_URI, "false"); + httpService.registerServlet(path, remotingServlet, ip, new PrivateHttpContext(httpRealm)); + } + + static String webdavPath(String alias) { + return NodeConstants.PATH_DATA + "/" + alias; + } + + static String remotingPath(String alias) { + return NodeConstants.PATH_JCR + "/" + alias; + } + + static String filesPath(String alias) { + return NodeConstants.PATH_FILES; + } + + // private Subject subjectFromRequest(HttpServletRequest request, + // HttpServletResponse response) { + // Authorization authorization = (Authorization) + // request.getAttribute(HttpContext.AUTHORIZATION); + // if (authorization == null) + // throw new CmsException("Not authenticated"); + // try { + // LoginContext lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, + // new HttpRequestCallbackHandler(request, response)); + // lc.login(); + // return lc.getSubject(); + // } catch (LoginException e) { + // throw new CmsException("Cannot login", e); + // } + // } + + static class RepositoriesStc extends ServiceTracker { + private final HttpService httpService; + + private final BundleContext bc; + + public RepositoriesStc(BundleContext bc, HttpService httpService) { + super(bc, Repository.class, null); + this.httpService = httpService; + this.bc = bc; + } + + @Override + public Repository addingService(ServiceReference reference) { + Repository repository = bc.getService(reference); + Object jcrRepoAlias = reference.getProperty(NodeConstants.CN); + if (jcrRepoAlias != null) { + String alias = jcrRepoAlias.toString(); + registerRepositoryServlets(httpService, alias, repository); + } + return repository; + } + + @Override + public void modifiedService(ServiceReference reference, Repository service) { + } + + @Override + public void removedService(ServiceReference reference, Repository service) { + Object jcrRepoAlias = reference.getProperty(NodeConstants.CN); + if (jcrRepoAlias != null) { + String alias = jcrRepoAlias.toString(); + unregisterRepositoryServlets(httpService, alias); + } + } + } + + private class PrepareHttpStc extends ServiceTracker { + // private DataHttp dataHttp; + // private NodeHttp nodeHttp; + + public PrepareHttpStc() { + super(bc, HttpService.class, null); + } + + @Override + public HttpService addingService(ServiceReference reference) { + long begin = System.currentTimeMillis(); + log.debug("HTTP prepare starts..."); + HttpService httpService = addHttpService(reference); + log.debug("HTTP prepare duration: " + (System.currentTimeMillis() - begin) + "ms"); + return httpService; + } + + @Override + public void removedService(ServiceReference reference, HttpService service) { + // if (dataHttp != null) + // dataHttp.destroy(); + // dataHttp = null; + // if (nodeHttp != null) + // nodeHttp.destroy(); + // nodeHttp = null; + // destroy(); + repositories.close(); + repositories = null; + } + + private HttpService addHttpService(ServiceReference sr) { + HttpService httpService = bc.getService(sr); + // TODO find constants + Object httpPort = sr.getProperty("http.port"); + Object httpsPort = sr.getProperty("https.port"); + + try { + httpService.registerServlet("/!", new LinkServlet(), null, null); + httpService.registerServlet("/robots.txt", new RobotServlet(), null, null); + } catch (Exception e) { + throw new CmsException("Cannot register filters", e); + } + // track repositories + if (repositories != null) + throw new CmsException("An http service is already configured"); + repositories = new RepositoriesStc(bc, httpService); + // repositories.open(); + KernelUtils.asyncOpen(repositories); + log.info(httpPortsMsg(httpPort, httpsPort)); + // httpAvailable = true; + // checkReadiness(); + + bc.registerService(NodeHttp.class, NodeHttp.this, null); + return httpService; + } + + private String httpPortsMsg(Object httpPort, Object httpsPort) { + return "HTTP " + httpPort + (httpsPort != null ? " - HTTPS " + httpsPort : ""); + } + } + + private static class WebdavServlet extends SimpleWebdavServlet { + private static final long serialVersionUID = -4687354117811443881L; + private final Repository repository; + + public WebdavServlet(Repository repository, SessionProvider sessionProvider) { + this.repository = repository; + setSessionProvider(sessionProvider); + } + + public Repository getRepository() { + return repository; + } + + @Override + protected void service(final HttpServletRequest request, final HttpServletResponse response) + throws ServletException, IOException { + WebdavServlet.super.service(request, response); + // try { + // Subject subject = subjectFromRequest(request); + // // TODO make it stronger, with eTags. + // // if (CurrentUser.isAnonymous(subject) && + // // request.getMethod().equals("GET")) { + // // response.setHeader("Cache-Control", "no-transform, public, + // // max-age=300, s-maxage=900"); + // // } + // + // Subject.doAs(subject, new PrivilegedExceptionAction() { + // @Override + // public Void run() throws Exception { + // WebdavServlet.super.service(request, response); + // return null; + // } + // }); + // } catch (PrivilegedActionException e) { + // throw new CmsException("Cannot process webdav request", + // e.getException()); + // } + } + + } + + private static class RemotingServlet extends JcrRemotingServlet { + private final Log log = LogFactory.getLog(RemotingServlet.class); + private static final long serialVersionUID = 4605238259548058883L; + private final Repository repository; + private final SessionProvider sessionProvider; + + public RemotingServlet(Repository repository, SessionProvider sessionProvider) { + this.repository = repository; + this.sessionProvider = sessionProvider; + } + + @Override + protected Repository getRepository() { + return repository; + } + + @Override + protected SessionProvider getSessionProvider() { + return sessionProvider; + } + + @Override + protected void service(final HttpServletRequest request, final HttpServletResponse response) + throws ServletException, IOException { + // try { + // Subject subject = subjectFromRequest(request, response); + // Subject.doAs(subject, new PrivilegedExceptionAction() { + // @Override + // public Void run() throws Exception { + if (log.isTraceEnabled()) + HttpUtils.logRequest(log, request); + RemotingServlet.super.service(request, response); + // return null; + // } + // }); + // } catch (PrivilegedActionException e) { + // throw new CmsException("Cannot process JCR remoting request", + // e.getException()); + // } + } + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java index 45ed8ec3e..134ab26ab 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java @@ -129,6 +129,10 @@ class NodeLogger implements ArgeoLogger, LogListener { // this.layout = layout; // } + public String toString() { + return "Node Logger"; + } + // // OSGi LOGGER // diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java index 077a1f8a7..f1132a642 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java @@ -36,7 +36,6 @@ import org.apache.commons.httpclient.params.HttpParams; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.cms.CmsException; -import org.argeo.cms.internal.http.NodeHttp; import org.argeo.cms.internal.http.client.HttpCredentialProvider; import org.argeo.cms.internal.http.client.SpnegoAuthScheme; import org.argeo.naming.DnsBrowser;