// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.osgi.boot; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.osgi.boot.internal.serverfactory.ServerInstanceWrapper; import org.eclipse.jetty.osgi.boot.utils.Util; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.util.tracker.ServiceTracker; /** * ServiceContextProvider * * Jetty DeploymentManager Provider that is able to deploy ContextHandlers discovered via OSGi as services. * * */ public class ServiceContextProvider extends AbstractContextProvider implements ServiceProvider { private static final Logger LOG = Log.getLogger(AbstractContextProvider.class); private Map<ServiceReference, App> _serviceMap = new HashMap<ServiceReference, App>(); private ServiceRegistration _serviceRegForServices; ServiceTracker _tracker; /** * ContextTracker * * */ public class ContextTracker extends ServiceTracker { public ContextTracker (BundleContext bundleContext, Filter filter) { super(bundleContext, filter, null); } /** * @see org.osgi.util.tracker.ServiceTracker#addingService(org.osgi.framework.ServiceReference) */ @Override public Object addingService(ServiceReference reference) { ContextHandler h = (ContextHandler)context.getService(reference); serviceAdded (reference, h); return h; } /** * @see org.osgi.util.tracker.ServiceTracker#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object) */ @Override public void modifiedService(ServiceReference reference, Object service) { removedService(reference,service); addingService(reference); } /** * @see org.osgi.util.tracker.ServiceTracker#removedService(org.osgi.framework.ServiceReference, java.lang.Object) */ @Override public void removedService(ServiceReference reference, Object service) { context.ungetService(reference); serviceRemoved(reference, (ContextHandler)service); } } /** * ServiceApp * * */ public class ServiceApp extends OSGiApp { public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId) { super(manager, provider, bundle, properties, contextFile, originId); } public ServiceApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile) { super(manager, provider, originId, bundle, contextFile); } @Override public void registerAsOSGiService() throws Exception { //not applicable for apps that are already services } @Override protected void deregisterAsOSGiService() throws Exception { //not applicable for apps that are already services } } /* ------------------------------------------------------------ */ public ServiceContextProvider(ServerInstanceWrapper wrapper) { super(wrapper); } /* ------------------------------------------------------------ */ public boolean serviceAdded (ServiceReference serviceRef, ContextHandler context) { if (context == null || serviceRef == null) return false; if (context instanceof org.eclipse.jetty.webapp.WebAppContext) return false; //the ServiceWebAppProvider will deploy it String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK); if (watermark != null && !"".equals(watermark)) return false; //this service represents a contexthandler that has already been registered as a service by another of our deployers ClassLoader cl = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps()); try { //See if there is a context file to apply to this pre-made context String contextFile = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (contextFile == null) contextFile = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH); String[] keys = serviceRef.getPropertyKeys(); Dictionary properties = new Hashtable<String, Object>(); if (keys != null) { for (String key:keys) properties.put(key, serviceRef.getProperty(key)); } Bundle bundle = serviceRef.getBundle(); String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-"+(contextFile!=null?contextFile:serviceRef.getProperty(Constants.SERVICE_ID)); ServiceApp app = new ServiceApp(getDeploymentManager(), this, bundle, properties, contextFile, originId); app.setHandler(context); //set the pre=made ContextHandler instance _serviceMap.put(serviceRef, app); getDeploymentManager().addApp(app); return true; } finally { Thread.currentThread().setContextClassLoader(cl); } } /* ------------------------------------------------------------ */ public boolean serviceRemoved (ServiceReference serviceRef, ContextHandler context) { if (context == null || serviceRef == null) return false; String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK); if (watermark != null && !"".equals(watermark)) return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers App app = _serviceMap.remove(serviceRef); if (app != null) { getDeploymentManager().removeApp(app); return true; } return false; } /* ------------------------------------------------------------ */ @Override protected void doStart() throws Exception { BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); //Start a tracker to find webapps that are osgi services that are targetted to my server name _tracker = new ContextTracker (bundleContext, Util.createFilter(bundleContext, ContextHandler.class.getName(), getServerInstanceWrapper().getManagedServerName())); _tracker.open(); //register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to Dictionary<String,String> properties = new Hashtable<String,String>(); properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName()); //register as an osgi service for deploying contexts, advertising the name of the jetty Server instance we are related to _serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties); super.doStart(); } /* ------------------------------------------------------------ */ @Override protected void doStop() throws Exception { if (_tracker != null) _tracker.close(); //unregister ourselves if (_serviceRegForServices != null) { try { _serviceRegForServices.unregister(); } catch (Exception e) { LOG.warn(e); } } super.doStop(); } }