/******************************************************************************* * Copyright (c) 2008 Cambridge Semantics Incorporated. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * File: $Source$ * Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>) * Created on: Jul 15, 2008 * Revision: $Id$ * * Contributors: * Cambridge Semantics Incorporated - initial API and implementation *******************************************************************************/ package org.openanzo.combus.bayeux; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Properties; import java.util.Set; import javax.servlet.Servlet; import javax.servlet.ServletException; import org.eclipse.jetty.servlets.GzipFilter; import org.openanzo.analysis.ProfilingServletFilter; import org.openanzo.cache.ICacheProvider; import org.openanzo.combus.IJmsProvider; import org.openanzo.combus.bayeux.attributes.BayeuxBridgeAttributes; import org.openanzo.datasource.ConfiguredWithPrimaryDatasourceActivator; import org.openanzo.datasource.IDatasourceListener; import org.openanzo.exceptions.AnzoException; import org.openanzo.exceptions.LogUtils; import org.openanzo.ontologies.openanzo.NamedGraph; import org.openanzo.osgi.GenericObjectClassDef; import org.openanzo.osgi.ServiceLifecycleState; import org.openanzo.osgi.attributes.CombusAttributes; import org.openanzo.osgi.attributes.ServicesAttributes; import org.openanzo.rdf.Statement; import org.openanzo.rdf.URI; import org.openanzo.security.keystore.ISecretKeystore; import org.openanzo.services.IAuthenticationService; import org.openanzo.services.IAuthorizationEventListener; import org.openanzo.services.ServicesDictionary; import org.openanzo.services.impl.ConfiguredCredentials; import org.openanzo.servlet.EncryptedTokenAuthenticator; import org.openanzo.servlet.PathSpec; import org.openanzo.servlet.SecurityConstraint; import org.openanzo.servlet.ServerRealm; import org.openanzo.servlet.WebcontainerTracker; import org.ops4j.pax.web.service.WebContainer; import org.osgi.framework.InvalidSyntaxException; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.metatype.AttributeDefinition; import org.osgi.service.metatype.ObjectClassDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Activator for the Bayeux listener * * @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com</a>) * */ public class BayeuxActivator extends ConfiguredWithPrimaryDatasourceActivator implements IDatasourceListener, IAuthorizationEventListener { private static final Logger log = LoggerFactory.getLogger("org.openanzo.combus.bayeux.BayeuxJmsBridge"); private Servlet servlet = null; private PrincipalFilter principalFilter = null; private GzipFilter gzipFilter = null; private ProfilingServletFilter profilingFilter = null; private BayeuxJMSBridge bridge = null; private String[] cometdPathSpec = new String[] { "/cometd/*" }; private ServerRealm relm = null; EncryptedTokenAuthenticator authenticator = null; Properties servletProperties = null; static final String SERVICE_PID = "org.openanzo.combus.BayeuxBridge"; GenericObjectClassDef classDef; HashMap<String, WebContainer> webContainers = new HashMap<String, WebContainer>(); WebcontainerTracker wcTracker; public ObjectClassDefinition getObjectClassDefinition(String id, String locale) { return classDef != null ? classDef : (classDef = new GenericObjectClassDef(SERVICE_PID, getBundleName(), getBundleDescription(), new AttributeDefinition[] { ServicesAttributes.Enabled, CombusAttributes.Host, CombusAttributes.Port, ServicesAttributes.User, ServicesAttributes.Password }, new AttributeDefinition[] { BayeuxBridgeAttributes.ThreadPoolSize })); } @Override public String getServicePid() { return SERVICE_PID; } @Override public String[] getDependencies() { return new String[] { IAuthenticationService.class.getName(), ISecretKeystore.class.getName(), IJmsProvider.class.getName(), ICacheProvider.class.getName() }; } public void reset() throws AnzoException { bridge.reset(); } public void resetFinished() throws AnzoException { bridge.resetFinished(); } public void resetStarting() throws AnzoException { bridge.resetStarting(); } @Override public boolean isInitialized() { return super.isInitialized() && webContainers.size() > 0; } @Override public void configurationPropertiesSet(Set<String> changedProps) throws ConfigurationException { if (configProperties != null && wcTracker == null) { String instanceIds = (String) configProperties.get("org.ops4j.pax.web.instanceId"); try { wcTracker = new WebcontainerTracker(context, instanceIds, new WebcontainerTracker.WebcontainerTrackerListener() { public void webcontainerUnAvailable(String instanceId, WebContainer wc) { webContainers.remove(instanceId); if (webContainers.size() == 0) { stopLocked(false); } } public void webcontainerAvailable(String instanceId, WebContainer wc) { webContainers.put(instanceId, wc); if (state != ServiceLifecycleState.STARTED) { if (isInitialized()) { startLocked(); } } else { try { registerWebContainer(wc); } catch (ServletException se) { log.error(LogUtils.LIFECYCLE_MARKER, "Error registering bayuex with webContainer", se); } } } }); wcTracker.open(); } catch (InvalidSyntaxException ise) { throw new ConfigurationException("org.ops4j.pax.web.instanceId", ise.getMessage()); } } } public void postReset() throws AnzoException { } @Override public void start() throws AnzoException { boolean enabled = ServicesDictionary.getEnabled(configProperties); if (enabled) { boolean reqSSL = false; Boolean requireSSL = ServicesDictionary.getRequireSSL(configProperties); if (requireSSL != null) { reqSSL = requireSSL.booleanValue(); } servletProperties = new Properties(); for (Enumeration<?> keys = configProperties.keys(); keys.hasMoreElements();) { Object key = keys.nextElement(); servletProperties.put(key, configProperties.get(key)); } primaryDatasource.registerDatasourceListener(this); servlet = new BayeuxJMSBridgeServlet(); servletProperties.setProperty("timeout", "60000"); servletProperties.setProperty("maxInterval", "30000"); servletProperties.setProperty("multiFrameInterval", "1500"); servletProperties.setProperty("logLevel", "1"); servletProperties.setProperty("JSONCommented", "false"); servletProperties.setProperty("requestAvailable", "true"); servletProperties.setProperty("asyncDeliver", "false"); // Removes a possible point of Bayeux message loss. See http://groups.google.com/group/cometd-dev/browse_thread/thread/b8cb84108a934073 ConfiguredCredentials credentials = new ConfiguredCredentials(servletProperties, null, null); bridge = new BayeuxJMSBridge(getDependency(IAuthenticationService.class), getDependency(IJmsProvider.class), getDependency(ICacheProvider.class), servletProperties, credentials, primaryDatasource); //HttpContext httpContext = httpService.createDefaultHttpContext(); relm = new ServerRealm(getDependency(IAuthenticationService.class)); authenticator = new EncryptedTokenAuthenticator(context, reqSSL ? SecurityConstraint.INTEGRAL : SecurityConstraint.NONE, relm, getDependency(ISecretKeystore.class), null, null, new ArrayList<PathSpec>() { private static final long serialVersionUID = 1L; { add(new PathSpec("/cometd/*")); } }); profilingFilter = new ProfilingServletFilter(); gzipFilter = new GzipFilter(); principalFilter = new PrincipalFilter(); for (WebContainer webContainer : webContainers.values()) { try { registerWebContainer(webContainer); } catch (ServletException se) { log.error(LogUtils.LIFECYCLE_MARKER, "Error registering bayuex servlet with webcontainer", se); } } } else { state = ServiceLifecycleState.NOT_ENABLED; } } private void registerWebContainer(WebContainer webContainer) throws ServletException { webContainer.registerEventListener(bridge, authenticator); webContainer.registerServlet(servlet, cometdPathSpec, servletProperties, authenticator); webContainer.registerFilter(profilingFilter, cometdPathSpec, null, servletProperties, authenticator); webContainer.registerFilter(gzipFilter, cometdPathSpec, null, servletProperties, authenticator); webContainer.registerFilter(principalFilter, cometdPathSpec, null, servletProperties, authenticator); } private void unregisterWebContainer(WebContainer webContainer) { if (bridge != null) webContainer.unregisterEventListener(bridge); if (servlet != null) webContainer.unregisterServlet(servlet); if (profilingFilter != null) webContainer.unregisterFilter(profilingFilter); if (gzipFilter != null) webContainer.unregisterFilter(gzipFilter); if (principalFilter != null) webContainer.unregisterFilter(principalFilter); } @Override public void stop(boolean bundleStopping) throws AnzoException { if (primaryDatasource != null) { primaryDatasource.unregisterDatasourceListener(this); } servlet.destroy(); for (WebContainer webContainer : webContainers.values()) { unregisterWebContainer(webContainer); } servlet = null; principalFilter = null; gzipFilter = null; if (bridge != null) { bridge.stop(bundleStopping); bridge = null; } if (wcTracker != null && bundleStopping) { wcTracker.close(); wcTracker = null; } } @Override public void restarted(Set<String> changedProps) { if (changedProps.contains(BayeuxBridgeDictionary.KEY_THREAD_POOL_SIZE)) { bridge.refreshThreadPoolSize(configProperties); } super.restarted(changedProps); } public void handleAuthorizationUpdates(Set<Statement> aclAdditions, Set<Statement> aclRemovals) throws AnzoException { if (aclRemovals != null && bridge != null) { for (Statement acl : aclRemovals) { URI namedGraphUri = (URI) acl.getSubject(); URI privilege = acl.getPredicate(); if (privilege.equals(NamedGraph.canBeReadByProperty)) { bridge.namedGraphReadPrivilegeRemoved(namedGraphUri, (URI) acl.getObject()); } } } } }