felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmesc...@apache.org
Subject svn commit: r1496453 - in /felix/trunk/http/jetty: ./ src/main/java/org/apache/felix/http/jetty/internal/ src/main/resources/OSGI-INF/metatype/
Date Tue, 25 Jun 2013 12:47:03 GMT
Author: fmeschbe
Date: Tue Jun 25 12:47:02 2013
New Revision: 1496453

URL: http://svn.apache.org/r1496453
Log:
FELIX-4137 Support Web Application Bundles

Apply patch by Dominique Pfister (thanks alot)

Added:
    felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/WebAppBundleContext.java
    felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/WebEvent.java
Modified:
    felix/trunk/http/jetty/pom.xml
    felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
    felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
    felix/trunk/http/jetty/src/main/resources/OSGI-INF/metatype/metatype.properties
    felix/trunk/http/jetty/src/main/resources/OSGI-INF/metatype/metatype.xml

Modified: felix/trunk/http/jetty/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/http/jetty/pom.xml?rev=1496453&r1=1496452&r2=1496453&view=diff
==============================================================================
--- felix/trunk/http/jetty/pom.xml (original)
+++ felix/trunk/http/jetty/pom.xml Tue Jun 25 12:47:02 2013
@@ -71,11 +71,13 @@
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.core</artifactId>
+            <version>4.2.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.compendium</artifactId>
+            <version>4.2.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -108,6 +110,11 @@
             <version>7.6.3.v20120416</version>
         </dependency>
         <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-webapp</artifactId>
+            <version>7.6.3.v20120416</version>
+        </dependency>
+        <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.http.api</artifactId>
             <version>2.2.0</version>

Modified: felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java?rev=1496453&r1=1496452&r2=1496453&view=diff
==============================================================================
--- felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
(original)
+++ felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
Tue Jun 25 12:47:02 2013
@@ -16,10 +16,13 @@
  */
 package org.apache.felix.http.jetty.internal;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Properties;
 
@@ -95,6 +98,9 @@ public final class JettyConfig
     /** Felix specific property to set the servlet context path of the Http Service */
     public static final String FELIX_HTTP_CONTEXT_PATH = "org.apache.felix.http.context_path";
 
+    /** Felix specific property to set the list of path exclusions for Web Application Bundles
*/
+    public static final String FELIX_HTTP_PATH_EXCLUSIONS = "org.apache.felix.http.path_exclusions";
+
     private final BundleContext context;
     private boolean debug;
     private String host;
@@ -116,6 +122,7 @@ public final class JettyConfig
     private int requestBufferSize;
     private int responseBufferSize;
     private String contextPath;
+    private String[] pathExclusions;
 
     /**
      * Properties from the configuration not matching any of the
@@ -247,6 +254,11 @@ public final class JettyConfig
         return contextPath;
     }
 
+    public String[] getPathExclusions()
+    {
+        return this.pathExclusions;
+    }
+
     public void reset()
     {
         update(null);
@@ -278,6 +290,7 @@ public final class JettyConfig
         this.requestBufferSize = getIntProperty(FELIX_JETTY_REQUEST_BUFFER_SIZE, 8 * 014);
         this.responseBufferSize = getIntProperty(FELIX_JETTY_RESPONSE_BUFFER_SIZE, 24 * 1024);
         this.contextPath = validateContextPath(getProperty(props, FELIX_HTTP_CONTEXT_PATH,
null));
+        this.pathExclusions = getStringArrayProperty(props, FELIX_HTTP_PATH_EXCLUSIONS, new
String[] { "/system" });
 
         // copy rest of the properties
         Enumeration keys = props.keys();
@@ -319,6 +332,41 @@ public final class JettyConfig
         }
     }
 
+    private String[] getStringArrayProperty(Dictionary props, String name, String[] defValue)
+    {
+        Object value = props.remove(name);
+        if (value == null)
+        {
+            value = this.context.getProperty(name);
+        }
+        if (value instanceof String)
+        {
+            return new String[]
+                { (String) value };
+        }
+        else if (value instanceof String[])
+        {
+            return (String[]) value;
+        }
+        else if (value instanceof Collection)
+        {
+            ArrayList<String> conv = new ArrayList<String>();
+            for (Iterator<?> vi = ((Collection<?>) value).iterator(); vi.hasNext();)
+            {
+                Object object = vi.next();
+                if (object != null)
+                {
+                    conv.add(String.valueOf(object));
+                }
+            }
+            return conv.toArray(new String[conv.size()]);
+        }
+        else
+        {
+            return defValue;
+        }
+    }
+
     private static String validateContextPath(String ctxPath)
     {
         // undefined, empty, or root context path

Modified: felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java?rev=1496453&r1=1496452&r2=1496453&view=diff
==============================================================================
--- felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
(original)
+++ felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
Tue Jun 25 12:47:02 2013
@@ -25,8 +25,14 @@ import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
 
 import org.apache.felix.http.base.internal.DispatcherServlet;
 import org.apache.felix.http.base.internal.EventDispatcher;
@@ -37,6 +43,7 @@ import org.eclipse.jetty.server.Connecto
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.SessionManager;
 import org.eclipse.jetty.server.bio.SocketConnector;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
 import org.eclipse.jetty.server.handler.StatisticsHandler;
 import org.eclipse.jetty.server.nio.SelectChannelConnector;
 import org.eclipse.jetty.server.ssl.SslConnector;
@@ -44,12 +51,26 @@ import org.eclipse.jetty.server.ssl.SslS
 import org.eclipse.jetty.server.ssl.SslSocketConnector;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
 import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+import javax.servlet.ServletContext;
 
 public final class JettyService
-    implements Runnable
+        extends AbstractLifeCycle.AbstractLifeCycleListener
+        implements BundleTrackerCustomizer, ServiceTrackerCustomizer
 {
     /** PID for configuration of the HTTP service. */
     private static final String PID = "org.apache.felix.http";
@@ -57,16 +78,27 @@ public final class JettyService
     /** Endpoint service registration property from RFC 189 */
     private static final String REG_PROPERTY_ENDPOINTS = "osgi.http.service.endpoints";
 
+    private static final String HEADER_WEB_CONTEXT_PATH = "Web-ContextPath";
+    private static final String HEADER_ACTIVATION_POLICY = "Bundle-ActivationPolicy";
+    private static final String WEB_SYMBOLIC_NAME = "osgi.web.symbolicname";
+    private static final String WEB_VERSION = "osgi.web.version";
+    private static final String WEB_CONTEXT_PATH = "osgi.web.contextpath";
+    private static final String OSGI_BUNDLE_CONTEXT = "osgi-bundlecontext";
+
     private final JettyConfig config;
     private final BundleContext context;
-    private boolean running;
-    private Thread thread;
     private ServiceRegistration configServiceReg;
+    private ExecutorService executor;
     private Server server;
+    private ContextHandlerCollection parent;
     private DispatcherServlet dispatcher;
     private EventDispatcher eventDispatcher;
     private final HttpServiceController controller;
     private MBeanServerTracker mbeanServerTracker;
+    private BundleTracker bundleTracker;
+    private ServiceTracker serviceTracker;
+    private EventAdmin eventAdmin;
+    private Map<String, Deployment> deployments = new LinkedHashMap<String, Deployment>();
 
     public JettyService(BundleContext context, DispatcherServlet dispatcher, EventDispatcher
eventDispatcher,
         HttpServiceController controller)
@@ -86,24 +118,48 @@ public final class JettyService
         this.configServiceReg = this.context.registerService("org.osgi.service.cm.ManagedService",
             new JettyManagedService(this), props);
 
-        this.thread = new Thread(this, "Jetty HTTP Service");
-        this.thread.start();
+        this.executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+           public Thread newThread(Runnable runnable)
+           {
+                Thread t = new Thread(runnable);
+                t.setName("Jetty HTTP Service");
+                return t;
+            }
+        });
+        this.executor.submit(new JettyOperation() {
+            @Override
+            protected void doExecute() throws Exception {
+                startJetty();
+            }
+        });
+
+        this.serviceTracker = new ServiceTracker(this.context, EventAdmin.class.getName(),
this);
+        this.serviceTracker.open();
+
+        this.bundleTracker = new BundleTracker(this.context, Bundle.ACTIVE | Bundle.STARTING,
this);
+        this.bundleTracker.open();
     }
 
     public void stop()
         throws Exception
     {
+        if (this.executor != null && !this.executor.isShutdown()) {
+            this.executor.submit(new JettyOperation() {
+                @Override
+                protected void doExecute() throws Exception {
+                    stopJetty();
+                }
+            });
+            this.executor.shutdown();
+        }
         if (this.configServiceReg != null) {
             this.configServiceReg.unregister();
         }
-
-        this.running = false;
-        this.thread.interrupt();
-
-        try {
-            this.thread.join(3000);
-        } catch (InterruptedException e) {
-            // Do nothing
+        if (this.bundleTracker != null) {
+            this.bundleTracker.close();
+        }
+        if (this.serviceTracker != null) {
+            this.serviceTracker.close();
         }
     }
 
@@ -119,8 +175,14 @@ public final class JettyService
     {
         this.config.update(props);
 
-        if (this.running && (this.thread != null)) {
-            this.thread.interrupt();
+        if (this.executor != null  && !this.executor.isShutdown()) {
+            this.executor.submit(new JettyOperation() {
+                @Override
+                protected void doExecute() throws Exception {
+                    stopJetty();
+                    startJetty();
+                }
+            });
         }
     }
 
@@ -163,6 +225,7 @@ public final class JettyService
             StringBuffer message = new StringBuffer("Started jetty ").append(Server.getVersion()).append("
at port(s)");
             HashLoginService realm = new HashLoginService("OSGi HTTP Service Realm");
             this.server = new Server();
+            this.server.addLifeCycleListener(this);
 
             // HTTP/1.1 requires Date header if possible (it is)
             this.server.setSendDateHeader(true);
@@ -181,7 +244,10 @@ public final class JettyService
                 message.append(" HTTPS:").append(this.config.getHttpsPort());
             }
 
-            ServletContextHandler context = new ServletContextHandler(this.server, this.config.getContextPath(),
ServletContextHandler.SESSIONS);
+            this.parent = new ContextHandlerCollection();
+
+            ServletContextHandler context = new ServletContextHandler(this.parent,
+                    this.config.getContextPath(), ServletContextHandler.SESSIONS);
 
             message.append(" on context path ").append(this.config.getContextPath());
             configureSessionManager(context);
@@ -196,6 +262,7 @@ public final class JettyService
                 context.addBean(new StatisticsHandler());
             }
 
+            this.server.setHandler(this.parent);
             this.server.start();
             SystemLogger.info(message.toString());
         }
@@ -342,31 +409,6 @@ public final class JettyService
         manager.setMaxCookieAge(this.config.getIntProperty(SessionManager.__MaxAgeProperty,
-1));
     }
 
-    public void run()
-    {
-        this.running = true;
-        Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
-
-        while (this.running) {
-            startJetty();
-
-            synchronized (this)
-            {
-                try
-                {
-                    wait();
-                }
-                catch (InterruptedException e)
-                {
-                    // we will definitely be interrupted
-                }
-            }
-
-            stopJetty();
-        }
-    }
-
-
     private String getEndpoint(final Connector listener, final InetAddress ia)
     {
         if (ia.isLoopbackAddress())
@@ -491,4 +533,266 @@ public final class JettyService
         }
         props.put(REG_PROPERTY_ENDPOINTS, endpoints.toArray(new String[endpoints.size()]));
     }
+
+    private Deployment startWebAppBundle(Bundle bundle, String contextPath)
+    {
+        postEvent(WebEvent.DEPLOYING(bundle, this.context.getBundle()));
+
+        // check existing deployments
+        Deployment deployment = this.deployments.get(contextPath);
+        if (deployment != null) {
+            SystemLogger.warning(String.format(
+                    "Web application bundle %s has context path %s which is already registered",
+                    bundle.getSymbolicName(), contextPath), null);
+            postEvent(WebEvent.FAILED(bundle, this.context.getBundle(), null, contextPath,
+                    deployment.getBundle().getBundleId()));
+            return null;
+        }
+
+        // check context path belonging to Http Service implementation
+        if (contextPath.equals("/")) {
+            SystemLogger.warning(String.format(
+                    "Web application bundle %s has context path %s which is reserved",
+                    bundle.getSymbolicName(), contextPath), null);
+            postEvent(WebEvent.FAILED(bundle, this.context.getBundle(), null, contextPath,
+                    this.context.getBundle().getBundleId()));
+            return null;
+        }
+
+        // check against excluded paths
+        for (String path : this.config.getPathExclusions()) {
+            if (contextPath.startsWith(path)) {
+                SystemLogger.warning(String.format(
+                        "Web application bundle %s has context path %s which clashes with
excluded path prefix %s",
+                        bundle.getSymbolicName(), contextPath, path), null);
+                postEvent(WebEvent.FAILED(bundle, this.context.getBundle(), null, path, null));
+                return null;
+            }
+        }
+
+        deployment = new Deployment(contextPath, bundle);
+        this.deployments.put(contextPath, deployment);
+
+        WebAppBundleContext context = new WebAppBundleContext(contextPath, bundle, this.getClass().getClassLoader());
+        deploy(deployment, context);
+        return deployment;
+    }
+
+    public void deploy(final Deployment deployment, final WebAppBundleContext context)
+    {
+        if (this.executor != null  && !this.executor.isShutdown()) {
+            this.executor.submit(new JettyOperation() {
+                @Override
+                protected void doExecute() {
+                    final Bundle webAppBundle = deployment.getBundle();
+                    final Bundle extenderBundle = JettyService.this.context.getBundle();
+
+                    try {
+                        JettyService.this.parent.addHandler(context);
+                        context.start();
+
+                        Dictionary<String, Object> props = new Hashtable<String,
Object>();
+                        props.put(WEB_SYMBOLIC_NAME, webAppBundle.getSymbolicName());
+                        props.put(WEB_VERSION, webAppBundle.getVersion());
+                        props.put(WEB_CONTEXT_PATH, deployment.getContextPath());
+                        deployment.setRegistration(webAppBundle.getBundleContext().registerService(
+                                ServletContext.class.getName(), context.getServletContext(),
props));
+
+                        context.getServletContext().setAttribute(OSGI_BUNDLE_CONTEXT, webAppBundle.getBundleContext());
+
+                        postEvent(WebEvent.DEPLOYED(webAppBundle, extenderBundle));
+                    } catch (Exception e) {
+                        SystemLogger.error(String.format("Deploying web application bundle
%s failed.", webAppBundle.getSymbolicName()), e);
+                        postEvent(WebEvent.FAILED(webAppBundle, extenderBundle, e, null,
null));
+                        deployment.setContext(null);
+                    }
+                }
+            });
+            deployment.setContext(context);
+        }
+    }
+
+    public void undeploy(final Deployment deployment, final WebAppBundleContext context)
+    {
+        if (this.executor != null  && !this.executor.isShutdown()) {
+            this.executor.submit(new JettyOperation(){
+                @Override
+                protected void doExecute() {
+                    final Bundle webAppBundle = deployment.getBundle();
+                    final Bundle extenderBundle = JettyService.this.context.getBundle();
+
+                    try {
+                        postEvent(WebEvent.UNDEPLOYING(webAppBundle, extenderBundle));
+
+                        context.getServletContext().removeAttribute(OSGI_BUNDLE_CONTEXT);
+
+                        ServiceRegistration registration = deployment.getRegistration();
+                        if (registration != null) {
+                            registration.unregister();
+                        }
+                        deployment.setRegistration(null);
+                        context.stop();
+                    } catch (Exception e) {
+                        SystemLogger.error(String.format("Undeploying web application bundle
%s failed.", webAppBundle.getSymbolicName()), e);
+                    } finally {
+                        postEvent(WebEvent.UNDEPLOYED(webAppBundle, extenderBundle));
+                    }
+                }
+            });
+        }
+        deployment.setContext(null);
+    }
+
+    public Object addingBundle(Bundle bundle, BundleEvent event)
+    {
+        return detectWebAppBundle(bundle);
+    }
+
+    public void modifiedBundle(Bundle bundle, BundleEvent event, Object object)
+    {
+        detectWebAppBundle(bundle);
+    }
+
+    private Object detectWebAppBundle(Bundle bundle)
+    {
+        if (bundle.getState() == Bundle.ACTIVE || (bundle.getState() == Bundle.STARTING &&
+                "Lazy".equals(bundle.getHeaders().get(HEADER_ACTIVATION_POLICY)))) {
+
+            String contextPath = (String) bundle.getHeaders().get(HEADER_WEB_CONTEXT_PATH);
+            if (contextPath != null) {
+                return startWebAppBundle(bundle, contextPath);
+            }
+        }
+        return null;
+    }
+
+    public void removedBundle(Bundle bundle, BundleEvent event, Object object)
+    {
+        String contextPath = (String) bundle.getHeaders().get(HEADER_WEB_CONTEXT_PATH);
+        if (contextPath == null) {
+            return;
+        }
+
+        Deployment deployment = this.deployments.remove(contextPath);
+        if (deployment != null && deployment.getContext() != null) {
+            // remove registration, since bundle is already stopping
+            deployment.setRegistration(null);
+            undeploy(deployment, deployment.getContext());
+        }
+    }
+
+    public Object addingService(ServiceReference reference)
+    {
+        Object service = this.context.getService(reference);
+        modifiedService(reference, service);
+        return service;
+    }
+
+    public void modifiedService(ServiceReference reference, Object service)
+    {
+        this.eventAdmin = (EventAdmin) service;
+    }
+
+    public void removedService(ServiceReference reference, Object service)
+    {
+        this.context.ungetService(reference);
+        this.eventAdmin = null;
+    }
+
+    private void postEvent(Event event)
+    {
+        if (this.eventAdmin != null) {
+            this.eventAdmin.postEvent(event);
+        }
+    }
+
+    public void lifeCycleStarted(LifeCycle event)
+    {
+        for (Deployment deployment : this.deployments.values()) {
+            if (deployment.getContext() == null) {
+                postEvent(WebEvent.DEPLOYING(deployment.getBundle(), this.context.getBundle()));
+                WebAppBundleContext context = new WebAppBundleContext(deployment.getContextPath(),
+                        deployment.getBundle(), this.getClass().getClassLoader());
+                deploy(deployment, context);
+            }
+        }
+    }
+
+    public void lifeCycleStopping(LifeCycle event)
+    {
+        for (Deployment deployment : this.deployments.values()) {
+            if (deployment.getContext() != null) {
+                undeploy(deployment, deployment.getContext());
+            }
+        }
+    }
+
+    /**
+     * A deployment represents a web application bundle that may or may not be deployed.
+     */
+    static class Deployment
+    {
+        private String contextPath;
+        private Bundle bundle;
+        private WebAppBundleContext context;
+        private ServiceRegistration registration;
+
+        public Deployment(String contextPath, Bundle bundle)
+        {
+            this.contextPath = contextPath;
+            this.bundle = bundle;
+        }
+
+        public Bundle getBundle()
+        {
+            return this.bundle;
+        }
+
+        public String getContextPath()
+        {
+            return this.contextPath;
+        }
+
+        public WebAppBundleContext getContext()
+        {
+            return this.context;
+        }
+
+        public void setContext(WebAppBundleContext context)
+        {
+            this.context = context;
+        }
+
+        public ServiceRegistration getRegistration()
+        {
+            return this.registration;
+        }
+
+        public void setRegistration(ServiceRegistration registration)
+        {
+            this.registration = registration;
+        }
+    }
+
+    /**
+     * A Jetty operation is executed with the context class loader set to this class's
+     * class loader.
+     */
+    abstract static class JettyOperation implements Callable<Void>
+    {
+        public Void call() throws Exception
+        {
+            ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+            try {
+                Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+                doExecute();
+                return null;
+            } finally {
+                Thread.currentThread().setContextClassLoader(cl);
+            }
+        }
+
+        protected abstract void doExecute() throws Exception;
+    }
 }

Added: felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/WebAppBundleContext.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/WebAppBundleContext.java?rev=1496453&view=auto
==============================================================================
--- felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/WebAppBundleContext.java
(added)
+++ felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/WebAppBundleContext.java
Tue Jun 25 12:47:02 2013
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.jetty.internal;
+
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.resource.URLResource;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+
+class WebAppBundleContext extends WebAppContext
+{
+    public WebAppBundleContext(String contextPath, final Bundle bundle, final ClassLoader
parent)
+    {
+        super(null, contextPath.substring(1), contextPath);
+
+        this.setBaseResource(new BundleURLResource(bundle.getEntry("/")));
+        this.setClassLoader(new ClassLoader(parent) {
+            @Override
+            protected Class<?> findClass(String s) throws ClassNotFoundException {
+                // Don't try to load classes from the bundle when it is not active
+                if (bundle.getState() == Bundle.ACTIVE) {
+                    try {
+                        return bundle.loadClass(s);
+                    } catch (ClassNotFoundException e) {
+                    }
+                }
+                return super.findClass(s);
+            }
+
+            @Override
+            protected URL findResource(String name) {
+                // Don't try to load resources from the bundle when it is not active
+                if (bundle.getState() == Bundle.ACTIVE) {
+                    URL url = bundle.getResource(name);
+                    if (url != null) {
+                        return url;
+                    }
+                }
+                return super.findResource(name);
+            }
+
+            @Override
+            @SuppressWarnings({"unchecked"})
+            protected Enumeration<URL> findResources(String name) throws IOException
{
+                // Don't try to load resources from the bundle when it is not active
+                if (bundle.getState() == Bundle.ACTIVE) {
+                    Enumeration<URL> urls = (Enumeration<URL>) bundle.getResources(name);
+                    if (urls != null) {
+                        return urls;
+                    }
+                }
+                return super.findResources(name);
+            }
+        });
+        this.setThrowUnavailableOnStartupException(true);
+    }
+
+    @Override
+    public Resource newResource(URL url) throws IOException
+    {
+        if (url == null) {
+            return null;
+        }
+        return new BundleURLResource(url);
+    }
+
+    static class BundleURLResource extends URLResource
+    {
+        BundleURLResource(URL url)
+        {
+            super(url, null);
+        }
+
+        @Override
+        public synchronized void release()
+        {
+            if (this._in != null) {
+                // Do not close this input stream: it would invalidate
+                // the associated zipfile's inflater and every future access
+                // to some bundle entry leads to an NPE with message
+                // "Inflater has been closed"
+                this._in = null;
+            }
+            super.release();
+        }
+
+        @Override
+        public Resource addPath(String path) throws MalformedURLException
+        {
+            if (path == null) {
+                return null;
+            }
+            path = URIUtil.canonicalPath(path);
+
+            URL url = new URL(URIUtil.addPaths(this._url.toExternalForm(), path));
+            return new BundleURLResource(url);
+        }
+
+        @Override
+        public File getFile() throws IOException
+        {
+            // not available as a file
+            return null;
+        }
+    }
+}

Added: felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/WebEvent.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/WebEvent.java?rev=1496453&view=auto
==============================================================================
--- felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/WebEvent.java
(added)
+++ felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/WebEvent.java
Tue Jun 25 12:47:02 2013
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.jetty.internal;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+public abstract class WebEvent
+{
+    private static final String TOPIC_WEB_EVENT = "org/osgi/service/web";
+    private static final String TOPIC_DEPLOYING = TOPIC_WEB_EVENT + "/DEPLOYING";
+    private static final String TOPIC_DEPLOYED = TOPIC_WEB_EVENT + "/DEPLOYED";
+    private static final String TOPIC_UNDEPLOYING = TOPIC_WEB_EVENT + "/UNDEPLOYING";
+    private static final String TOPIC_UNDEPLOYED = TOPIC_WEB_EVENT + "/UNDEPLOYED";
+    private static final String TOPIC_FAILED = TOPIC_WEB_EVENT + "/FAILED";
+
+    private static final String CONTEXT_PATH = "context.path";
+    private static final String EXCEPTION = "exception";
+    private static final String COLLISION = "collision";
+    private static final String COLLISION_BUNDLES = "collision.bundles";
+
+    private static final String EXTENDER_BUNDLE = "extender." + EventConstants.BUNDLE;
+    private static final String EXTENDER_BUNDLE_ID = "extender." + EventConstants.BUNDLE_ID;
+    private static final String EXTENDER_BUNDLE_VERSION = "extender." + EventConstants.BUNDLE_VERSION;
+    private static final String EXTENDER_BUNDLE_SYMBOLICNAME = "extender." + EventConstants.BUNDLE_SYMBOLICNAME;
+
+    private static final String HEADER_WEB_CONTEXT_PATH = "Web-ContextPath";
+
+    static Event DEPLOYING(Bundle webAppBundle, Bundle extenderBundle)
+    {
+        return new Event(TOPIC_DEPLOYING, createBaseProperties(webAppBundle, extenderBundle));
+    }
+
+    static Event DEPLOYED(Bundle webAppBundle, Bundle extenderBundle)
+    {
+        return new Event(TOPIC_DEPLOYED, createBaseProperties(webAppBundle, extenderBundle));
+    }
+
+    static Event UNDEPLOYING(Bundle webAppBundle, Bundle extenderBundle)
+    {
+        return new Event(TOPIC_UNDEPLOYING, createBaseProperties(webAppBundle, extenderBundle));
+    }
+
+    static Event UNDEPLOYED(Bundle webAppBundle, Bundle extenderBundle)
+    {
+        return new Event(TOPIC_UNDEPLOYED, createBaseProperties(webAppBundle, extenderBundle));
+    }
+
+    static Event FAILED(Bundle webAppBundle, Bundle extenderBundle, Throwable exception,
+                        String collision, Long collisionBundles)
+    {
+        Dictionary<String,Object> props = createBaseProperties(webAppBundle, extenderBundle);
+        if (exception != null) {
+            props.put(EXCEPTION, exception);
+        }
+        if (collision != null) {
+            props.put(COLLISION, collision);
+        }
+        if (collisionBundles != null) {
+            props.put(COLLISION_BUNDLES, collisionBundles);
+        }
+        return new Event(TOPIC_FAILED, props);
+    }
+
+    private static Dictionary<String,Object> createBaseProperties(Bundle webAppBundle,
Bundle extenderBundle)
+    {
+        Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put(EventConstants.BUNDLE_SYMBOLICNAME, webAppBundle.getSymbolicName());
+        props.put(EventConstants.BUNDLE_ID, webAppBundle.getBundleId());
+        props.put(EventConstants.BUNDLE, webAppBundle);
+        props.put(EventConstants.BUNDLE_VERSION, webAppBundle.getVersion());
+        props.put(CONTEXT_PATH, webAppBundle.getHeaders().get(HEADER_WEB_CONTEXT_PATH));
+        props.put(EventConstants.TIMESTAMP, System.currentTimeMillis());
+        props.put(EXTENDER_BUNDLE, extenderBundle);
+        props.put(EXTENDER_BUNDLE_ID, extenderBundle.getBundleId());
+        props.put(EXTENDER_BUNDLE_SYMBOLICNAME, extenderBundle.getSymbolicName());
+        props.put(EXTENDER_BUNDLE_VERSION, extenderBundle.getVersion());
+        return props;
+    }
+}

Modified: felix/trunk/http/jetty/src/main/resources/OSGI-INF/metatype/metatype.properties
URL: http://svn.apache.org/viewvc/felix/trunk/http/jetty/src/main/resources/OSGI-INF/metatype/metatype.properties?rev=1496453&r1=1496452&r2=1496453&view=diff
==============================================================================
--- felix/trunk/http/jetty/src/main/resources/OSGI-INF/metatype/metatype.properties (original)
+++ felix/trunk/http/jetty/src/main/resources/OSGI-INF/metatype/metatype.properties Tue Jun
25 12:47:02 2013
@@ -122,4 +122,10 @@ org.apache.felix.http.context_path.name 
 org.apache.felix.http.context_path.description = The Servlet Context Path \
  to use for the Http Service. If this property is not configured it \
  defaults to "/". This must be a valid path starting with a slash and not \
- ending with a slash (unless it is the root context).
\ No newline at end of file
+ ending with a slash (unless it is the root context).
+
+org.apache.felix.http.path_exclusions.name = Path Exclusions
+org.apache.felix.http.path_exclusions.description = Contains a list of \
+ context path prefixes. If a Web Application Bundle is started with a \
+ context path matching any of these prefixes, it will not be deployed \
+ in the servlet container.
\ No newline at end of file

Modified: felix/trunk/http/jetty/src/main/resources/OSGI-INF/metatype/metatype.xml
URL: http://svn.apache.org/viewvc/felix/trunk/http/jetty/src/main/resources/OSGI-INF/metatype/metatype.xml?rev=1496453&r1=1496452&r2=1496453&view=diff
==============================================================================
--- felix/trunk/http/jetty/src/main/resources/OSGI-INF/metatype/metatype.xml (original)
+++ felix/trunk/http/jetty/src/main/resources/OSGI-INF/metatype/metatype.xml Tue Jun 25 12:47:02
2013
@@ -44,6 +44,7 @@
         <AD id="org.apache.felix.http.jetty.requestBufferSize" type="Integer" default="8192"
name="%org.apache.felix.http.jetty.requestBufferSize.name" description="%org.apache.felix.http.jetty.requestBufferSize.description"/>
         <AD id="org.apache.felix.http.jetty.responseBufferSize" type="Integer" default="24576"
name="%org.apache.felix.http.jetty.responseBufferSize.name" description="%org.apache.felix.http.jetty.responseBufferSize.description"/>
         <AD id="org.apache.felix.http.debug" type="Boolean" default="false" name="%org.apache.felix.http.debug.name"
description="%org.apache.felix.http.debug.description"/>
+        <AD id="org.apache.felix.http.path_exclusions" type="String" default="/system"
cardinality="2147483647" name="%org.apache.felix.http.path_exclusions.name" description="%org.apache.felix.http.path_exclusions.description"/>
     </OCD>
     <Designate pid="org.apache.felix.http">
         <Object ocdref="org.apache.felix.http"/>



Mime
View raw message