You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ri...@apache.org on 2009/12/16 15:22:05 UTC

svn commit: r891246 - in /geronimo/sandbox/rick: ./ rfc66/ rfc66/src/ rfc66/src/main/ rfc66/src/main/java/ rfc66/src/main/java/org/ rfc66/src/main/java/org/apache/ rfc66/src/main/java/org/apache/geronimo/ rfc66/src/main/java/org/apache/geronimo/osgi/ r...

Author: rickmcguire
Date: Wed Dec 16 14:22:04 2009
New Revision: 891246

URL: http://svn.apache.org/viewvc?rev=891246&view=rev
Log:
Some initial work on a Geronimo RFC 66 webcontainer.  This probably should be redone using a different 
approach, and some of this is already obsolete (it predates the merge of djencks initial OSGi 
restructuring). 


Added:
    geronimo/sandbox/rick/
    geronimo/sandbox/rick/rfc66/
    geronimo/sandbox/rick/rfc66/src/
    geronimo/sandbox/rick/rfc66/src/main/
    geronimo/sandbox/rick/rfc66/src/main/java/
    geronimo/sandbox/rick/rfc66/src/main/java/org/
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/AbstractOSGiWebAppContext.java   (with props)
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/TomcatOSGiWebAppContext.java   (with props)
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebApplicationImpl.java   (with props)
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerEventDispatcher.java   (with props)
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerExtender.java   (with props)
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/jetty/
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebAppContext.java   (with props)
    geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebContainer.java   (with props)

Added: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/AbstractOSGiWebAppContext.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/AbstractOSGiWebAppContext.java?rev=891246&view=auto
==============================================================================
--- geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/AbstractOSGiWebAppContext.java (added)
+++ geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/AbstractOSGiWebAppContext.java Wed Dec 16 14:22:04 2009
@@ -0,0 +1,475 @@
+/*
+ * 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.geronimo.osgi.web;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.transaction.UserTransaction;
+
+import org.apache.InstanceManager;
+import org.apache.catalina.Context;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Valve;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.geronimo.connector.outbound.connectiontracking.TrackedConnectionAssociator;
+import org.apache.geronimo.kernel.Kernel;
+import org.apache.geronimo.kernel.osgi.BundleClassLoader;
+import org.apache.geronimo.tomcat.util.SecurityHolder;
+import org.apache.tomcat.TomcatContext;
+
+/**
+ * Base class for implementing web container-specific
+ * OSGi contexts.
+ * @version $Rev$ $Date$
+ */
+public class AbstractOSGiWebAppContext {
+    // the bundle where this application resides
+    protected Bundle bundle;
+    // the fully resolved context path
+    protected String path;
+    // our wrappered class loader
+    protected ClassLoader classLoader;
+    // The parsed web.xml descriptor
+    protected WebApp webApp;
+    // Indicates whether this is using javaee
+    protected boolean isJavaee = false;
+    // Our ClassFinder instance for processing annotations.
+    protected ClassFinder classFinder;
+
+    public AbstractOSGiWebAppContext(Bundle bundle, String contextPath) {
+        this.bundle = bundle;
+        this.path = contextPath.startsWith("/") ? contextPath : "/" + contextPath;
+        // we give the container a classloader wrapped around the bundle
+        this.classLoader = new BundleClassLoader(bundle);
+        // process the WEB-INF/web.xml file and perform some initial validation.
+        parseWebXml();
+    }
+
+
+    /**
+     * Locate and parse a WEB-INF/web.xml file located in
+     * a WAB.
+     */
+    protected void parseWebXml() {
+        try {
+            // look for a web.xml file in the bundle
+            URL specDDUrl = bundle.getEntry("WEB-INF/web.xml");
+
+            // read in the entire specDD as a string, we need this for getDeploymentDescriptor
+            // on the J2ee management object
+            String specDD = DeploymentUtil.readAll(specDDUrl);
+
+            // we found web.xml, if it won't parse that's an error.
+            XmlObject parsed = XmlBeansUtil.parse(specDD);
+            //Dont save updated xml if it isn't javaee
+            XmlCursor cursor = parsed.newCursor();
+            try {
+                cursor.toStartDoc();
+                cursor.toFirstChild();
+                isJavaee = "http://java.sun.com/xml/ns/javaee".equals(cursor.getName().getNamespaceURI());
+            } finally {
+                cursor.dispose();
+            }
+            WebAppDocument webAppDoc = convertToServletSchema(parsed);
+            webApp = webAppDoc.getWebApp();
+            check(webApp);
+        } catch (XmlException e) {
+            // Output the bundle informatin in the error
+            throw new DeploymentException("Error parsing web.xml for " + bundle, e);
+        } catch (Exception e) {
+            isJavaee = true;
+            //else ignore as jee5 allows optional spec dd for .war's
+        }
+
+        // if there was no file, simplify by using a default instance.
+        if (webApp == null)
+            webApp = WebAppType.Factory.newInstance();
+    }
+
+
+    /**
+     * Normalize the document to use the 2.5 servlet specification.
+     *
+     * @param xmlObject The source input file.
+     *
+     * @return A normalized XML document.
+     * @exception XmlException
+     */
+    protected WebAppDocument convertToServletSchema(XmlObject xmlObject) throws XmlException {
+
+        String schemaLocationURL = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd";
+        String version = "2.5";
+        XmlCursor cursor = xmlObject.newCursor();
+        try {
+            cursor.toStartDoc();
+            cursor.toFirstChild();
+            if ("http://java.sun.com/xml/ns/j2ee".equals(cursor.getName().getNamespaceURI())) {
+                SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
+                XmlObject result = xmlObject.changeType(WebAppDocument.type);
+                XmlBeansUtil.validateDD(result);
+                return (WebAppDocument) result;
+            }
+
+            if ("http://java.sun.com/xml/ns/javaee".equals(cursor.getName().getNamespaceURI())) {
+                SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
+                XmlObject result = xmlObject.changeType(WebAppDocument.type);
+                XmlBeansUtil.validateDD(result);
+                return (WebAppDocument) result;
+            }
+
+            //otherwise assume DTD
+            XmlDocumentProperties xmlDocumentProperties = cursor.documentProperties();
+            String publicId = xmlDocumentProperties.getDoctypePublicId();
+            boolean is22 = "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN".equals(publicId);
+            if ("-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN".equals(publicId) ||
+                    is22) {
+                XmlCursor moveable = xmlObject.newCursor();
+                try {
+                    moveable.toStartDoc();
+                    moveable.toFirstChild();
+
+                    SchemaConversionUtils.convertToSchema(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version);
+                    cursor.toStartDoc();
+                    cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "web-app");
+                    cursor.toFirstChild();
+                    SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
+                    SchemaConversionUtils.convertToJNDIEnvironmentRefsGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
+                    cursor.push();
+                    if (cursor.toNextSibling(TAGLIB)) {
+                        cursor.toPrevSibling();
+                        moveable.toCursor(cursor);
+                        cursor.beginElement("jsp-config", SchemaConversionUtils.JAVAEE_NAMESPACE);
+                        while (moveable.toNextSibling(TAGLIB)) {
+                            moveable.moveXml(cursor);
+                        }
+                    }
+                    cursor.pop();
+                    do {
+                        String name = cursor.getName().getLocalPart();
+                        if ("filter".equals(name) || "servlet".equals(name) || "context-param".equals(name)) {
+                            cursor.push();
+                            cursor.toFirstChild();
+                            SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
+                            while (cursor.toNextSibling(SchemaConversionUtils.JAVAEE_NAMESPACE, "init-param")) {
+                                cursor.push();
+                                cursor.toFirstChild();
+                                SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
+                                cursor.pop();
+                            }
+                            cursor.pop();
+                            cursor.push();
+                            if (cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "jsp-file")) {
+                                String jspFile = cursor.getTextValue();
+                                if (!jspFile.startsWith("/")){
+                                    if (is22) {
+                                        cursor.setTextValue("/" + jspFile);
+                                    } else {
+                                        throw new XmlException("jsp-file does not start with / and this is not a 2.2 web app: " + jspFile);
+                                    }
+                                }
+                            }
+                            cursor.pop();
+                        }
+                    } while (cursor.toNextSibling());
+                } finally {
+                    moveable.dispose();
+                }
+            }
+        } finally {
+            cursor.dispose();
+        }
+        XmlObject result = xmlObject.changeType(WebAppDocument.type);
+        if (result != null) {
+            XmlBeansUtil.validateDD(result);
+            return (WebAppDocument) result;
+        }
+        XmlBeansUtil.validateDD(xmlObject);
+        return (WebAppDocument) xmlObject;
+    }
+
+    /**
+     * Perform some validation on the web.xml information.
+     *
+     * @param webApp The source configuration file information.
+     *
+     * @exception DeploymentException
+     */
+    protected static void check(WebAppType webApp) throws DeploymentException {
+        checkURLPattern(webApp);
+        checkMultiplicities(webApp);
+    }
+
+    /**
+     * Validate that the urlPatterns defined in the configuration
+     * file conform to URL constraints.
+     *
+     * @param webApp The source configuration data.
+     *
+     * @exception DeploymentException
+     */
+    private static void checkURLPattern(WebAppType webApp) throws DeploymentException {
+
+        FilterMappingType[] filterMappings = webApp.getFilterMappingArray();
+        for (FilterMappingType filterMapping : filterMappings) {
+            UrlPatternType[] urlPatterns = filterMapping.getUrlPatternArray();
+            for (int j = 0; (urlPatterns != null) && (j < urlPatterns.length); j++) {
+                checkString(urlPatterns[j].getStringValue().trim());
+            }
+        }
+
+        ServletMappingType[] servletMappings = webApp.getServletMappingArray();
+        for (ServletMappingType servletMapping : servletMappings) {
+            UrlPatternType[] urlPatterns = servletMapping.getUrlPatternArray();
+            for (int j = 0; (urlPatterns != null) && (j < urlPatterns.length); j++) {
+                checkString(urlPatterns[j].getStringValue().trim());
+            }
+        }
+
+        SecurityConstraintType[] constraints = webApp.getSecurityConstraintArray();
+        for (SecurityConstraintType constraint : constraints) {
+            WebResourceCollectionType[] collections = constraint.getWebResourceCollectionArray();
+            for (WebResourceCollectionType collection : collections) {
+                UrlPatternType[] patterns = collection.getUrlPatternArray();
+                for (UrlPatternType pattern : patterns) {
+                    checkString(pattern.getStringValue().trim());
+                }
+            }
+        }
+    }
+
+    protected static void checkString(String pattern) throws DeploymentException {
+        //j2ee_1_4.xsd explicitly requires preserving all whitespace. Do not trim.
+        if (pattern.indexOf(0x0D) >= 0) throw new DeploymentException("<url-pattern> must not contain CR(#xD)");
+        if (pattern.indexOf(0x0A) >= 0) throw new DeploymentException("<url-pattern> must not contain LF(#xA)");
+    }
+
+    private static void checkMultiplicities(WebAppType webApp) throws DeploymentException {
+        if (webApp.getSessionConfigArray().length > 1)
+            throw new DeploymentException("Multiple <session-config> elements found");
+        if (webApp.getJspConfigArray().length > 1)
+            throw new DeploymentException("Multiple <jsp-config> elements found");
+        if (webApp.getLoginConfigArray().length > 1)
+            throw new DeploymentException("Multiple <login-config> elements found");
+    }
+
+    protected void configureBasicWebModuleAttributes() throws DeploymentException {
+        if (!webApp.getMetadataComplete()) {
+            // Create a classfinder and populate it for processing the configuration.
+            // classFinder in the module will convey whether metadata-complete is set (or not)
+            classFinder = createWebAppClassFinder(webApp, webModule);
+        }
+        // do we have potential annotations to process?  Go look for security annotations
+        if (classFinder != null) {
+            SecurityAnnotationHelper.processAnnotations(webApp, classFinder);
+        }
+        //N.B. we use the ear context which has all the gbeans we could possibly be looking up from this ear.
+        //nope, persistence units can be in the war.
+        //This means that you cannot use the default environment of the web builder to add configs that will be searched.
+        getNamingBuilders().buildNaming(webApp, vendorPlan, webModule, buildingContext);
+
+        Map compContext = NamingBuilder.JNDI_KEY.get(buildingContext);
+        Holder holder = NamingBuilder.INJECTION_KEY.get(buildingContext);
+
+        webModule.getSharedContext().put(WebModule.WEB_APP_DATA, webModuleData);
+        webModule.getSharedContext().put(NamingBuilder.JNDI_KEY, compContext);
+        webModule.getSharedContext().put(NamingBuilder.INJECTION_KEY, holder);
+        if (moduleContext.getServerName() != null) {
+            webModuleData.setReferencePattern("J2EEServer", moduleContext.getServerName());
+        }
+        if (!webModule.isStandAlone()) {
+            webModuleData.setReferencePattern("J2EEApplication", earContext.getModuleName());
+        }
+
+        webModuleData.setAttribute("holder", holder);
+
+        //Add dependencies on managed connection factories and ejbs in this app
+        //This is overkill, but allows for people not using java:comp context (even though we don't support it)
+        //and sidesteps the problem of circular references between ejbs.
+        if (earContext != moduleContext) {
+            addGBeanDependencies(earContext, webModuleData);
+        }
+
+        webModuleData.setAttribute("componentContext", compContext);
+        webModuleData.setReferencePattern("TransactionManager", moduleContext.getTransactionManagerName());
+        webModuleData.setReferencePattern("TrackedConnectionAssociator", moduleContext.getConnectionTrackerName());
+    }
+
+
+    /**
+     * Retrieve the fully resolved context path for this instance.
+     *
+     * @return The fully resolved context path.
+     */
+    public String getContextPath() {
+        return path;
+    }
+
+    /**
+     * Set the container context for this application.  This is
+     * is set by the container when the app is deployed.
+     *
+     * @param ctx    The new context for this application.
+     */
+    public void setContext(Context ctx) {
+        context = ctx;
+        // set the bundle context attribute in the servlet context
+        context.getServletContainer().setAttribute(ATTRIBUTE_BUNDLE_CONTEXT, bundle.getBundleContext());
+        if (context instanceof StandardContext) {
+            statsProvider =  new ModuleStats((StandardContext)context);
+        }
+    }
+
+    /**
+     * Retrieve the deployment context for this application.
+     *
+     * @return The container-provided deployment context.
+     */
+    public Context getContext() {
+        return context,
+    }
+
+    /**
+     * Return the docbase for this application context.
+     *
+     * @return Always returns "" since we can't portably map this to a file
+     *         location.
+     */
+    public String getDocBase() {
+        return "";        // the bundle is installed, and doesn't really have a docbase location
+    }
+
+    public SecurityHolder getSecurityHolder() {
+        // we don't have a security holder to work about
+        return null;
+    }
+
+    public void String getVirtualServer() {
+        // no virtual server either
+        return null;
+    }
+
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    public UserTransaction getUserTransaction() {
+        return null;
+    }
+
+    public javax.naming.Context getJndiContext() {
+        return null;
+    }
+
+    public Kernel getKernel() {
+        return null;
+    }
+
+    public Set getApplicationManagedSecurityResources() {
+        return null;
+    }
+
+    public TrackedConnectionAssociator getTrackedConnectionAssociator() {
+        return null;
+    }
+
+    public Set getUnshareableResources() {
+        return null;
+    }
+
+    public Realm getRealm() {
+        return null;
+    }
+
+    public Valve getClusteredValve() {
+        return null;
+    }
+
+    public List getValveChain() {
+        return null;
+    }
+
+    public List getLifecycleListenerChain() {
+        return null;
+    }
+
+    public CatalinaCluster getCluster() {
+        return null;
+    }
+
+    public Manager getManager() {
+        return null;
+    }
+
+    public boolean isCrossContext() {
+        return false;
+    }
+
+    public String getWorkDir() {
+        return workDir;
+    }
+
+    public boolean isDisableCookies() {
+        return false;
+    }
+
+    public Map getWebServices() {
+        return null;
+    }
+
+    /**
+     * Create an instance manager for this WAB context.  This
+     * is basically an empty context that's dummied up.
+     *
+     * @return A dummy, empty, InstanceManager instance.
+     */
+    public InstanceManager getInstanceManager() {
+        return new TomcatInstanceManager(holder, classLoader, new ImmutableContext(new HashMap(), false));
+    }
+
+    //  JSR 77 statistics - The static values are initialized at the time of
+    //  creation, getStats return fresh value everytime
+    public Stats getStats() {
+        if (resetStats) {
+            resetStats = false;
+            return statsProvider.getStats();
+        }
+        else {
+            return statsProvider.updateStats();
+        }
+    }
+
+    public void resetStats() {
+        resetStats = true;
+    }
+
+
+    /**
+     * Perform resource cleanup when the servlet context is
+     * destroyed.
+     */
+    public destroy() {
+        statsProvider = null;
+    }
+}
+

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/AbstractOSGiWebAppContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/AbstractOSGiWebAppContext.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/AbstractOSGiWebAppContext.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/TomcatOSGiWebAppContext.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/TomcatOSGiWebAppContext.java?rev=891246&view=auto
==============================================================================
--- geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/TomcatOSGiWebAppContext.java (added)
+++ geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/TomcatOSGiWebAppContext.java Wed Dec 16 14:22:04 2009
@@ -0,0 +1,235 @@
+/*
+ * 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.geronimo.osgi.web.extender;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.transaction.UserTransaction;
+
+import org.apache.InstanceManager;
+import org.apache.catalina.Context;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Valve;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.geronimo.connector.outbound.connectiontracking.TrackedConnectionAssociator;
+import org.apache.geronimo.kernel.Kernel;
+import org.apache.geronimo.kernel.osgi.BundleClassLoader;
+import org.apache.geronimo.tomcat.util.SecurityHolder;
+import org.apache.tomcat.TomcatContext;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class TomcatOSGiWebAppContext implements TomcatContext, StatisticsProvider {
+    // the bundle where this application resides
+    protected Bundle bundle;
+    // the fully resolved context path
+    protected String path;
+    // the Tomcat-specific context
+    protected Context context = context;
+    // our wrappered class loader
+    protected ClassLoader classLoader;
+    // the working directory for JSP compilation
+    protected File workDir;
+    // a Holder used for annotation processing.  We just
+    // create a default empty one
+    protected final Holder holder;
+    //  used for managing the usage statistics
+    private ModuleStats statsProvider;
+    private boolean resetStats = true;
+
+
+
+    public TomcatOSGiWebAppContext(Bundle bundle, String contextPath) {
+        this.bundle = bundle;
+        this.path = contextPath.startsWith("/") ? contextPath : "/" + contextPath;
+        // we give the container a classloader wrapped around the bundle
+        this.classLoader = new BundleClassLoader(bundle);
+
+        // get the root of the framework provided persistence area
+        File base = bundle.getBundleContext().getDataFile("");
+        // create a working directory for this bundle, and attempt to create it.
+        workDir = new File(base, "webApp");
+        workDir.mkdir();
+        // the container requires this, but a default empty one
+        // is sufficient
+        this.holder = new Holder();
+    }
+
+    /**
+     * Retrieve the fully resolved context path for this instance.
+     *
+     * @return The fully resolved context path.
+     */
+    public String getContextPath() {
+        return path;
+    }
+
+    /**
+     * Set the container context for this application.  This is
+     * is set by the container when the app is deployed.
+     *
+     * @param ctx    The new context for this application.
+     */
+    public void setContext(Context ctx) {
+        context = ctx;
+        // set the bundle context attribute in the servlet context
+        context.getServletContainer().setAttribute(ATTRIBUTE_BUNDLE_CONTEXT, bundle.getBundleContext());
+        if (context instanceof StandardContext) {
+            statsProvider =  new ModuleStats((StandardContext)context);
+        }
+    }
+
+    /**
+     * Retrieve the deployment context for this application.
+     *
+     * @return The container-provided deployment context.
+     */
+    public Context getContext() {
+        return context,
+    }
+
+    /**
+     * Return the docbase for this application context.
+     *
+     * @return Always returns "" since we can't portably map this to a file
+     *         location.
+     */
+    public String getDocBase() {
+        return "";        // the bundle is installed, and doesn't really have a docbase location
+    }
+
+    public SecurityHolder getSecurityHolder() {
+        // we don't have a security holder to work about
+        return null;
+    }
+
+    public void String getVirtualServer() {
+        // no virtual server either
+        return null;
+    }
+
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    public UserTransaction getUserTransaction() {
+        return null;
+    }
+
+    public javax.naming.Context getJndiContext() {
+        return null;
+    }
+
+    public Kernel getKernel() {
+        return null;
+    }
+
+    public Set getApplicationManagedSecurityResources() {
+        return null;
+    }
+
+    public TrackedConnectionAssociator getTrackedConnectionAssociator() {
+        return null;
+    }
+
+    public Set getUnshareableResources() {
+        return null;
+    }
+
+    public Realm getRealm() {
+        return null;
+    }
+
+    public Valve getClusteredValve() {
+        return null;
+    }
+
+    public List getValveChain() {
+        return null;
+    }
+
+    public List getLifecycleListenerChain() {
+        return null;
+    }
+
+    public CatalinaCluster getCluster() {
+        return null;
+    }
+
+    public Manager getManager() {
+        return null;
+    }
+
+    public boolean isCrossContext() {
+        return false;
+    }
+
+    public String getWorkDir() {
+        return workDir;
+    }
+
+    public boolean isDisableCookies() {
+        return false;
+    }
+
+    public Map getWebServices() {
+        return null;
+    }
+
+    /**
+     * Create an instance manager for this WAB context.  This
+     * is basically an empty context that's dummied up.
+     *
+     * @return A dummy, empty, InstanceManager instance.
+     */
+    public InstanceManager getInstanceManager() {
+        return new TomcatInstanceManager(holder, classLoader, new ImmutableContext(new HashMap(), false));
+    }
+
+    //  JSR 77 statistics - The static values are initialized at the time of
+    //  creation, getStats return fresh value everytime
+    public Stats getStats() {
+        if (resetStats) {
+            resetStats = false;
+            return statsProvider.getStats();
+        }
+        else {
+            return statsProvider.updateStats();
+        }
+    }
+
+    public void resetStats() {
+        resetStats = true;
+    }
+
+
+    /**
+     * Perform resource cleanup when the servlet context is
+     * destroyed.
+     */
+    public destroy() {
+        statsProvider = null;
+    }
+}

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/TomcatOSGiWebAppContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/TomcatOSGiWebAppContext.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/TomcatOSGiWebAppContext.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebApplicationImpl.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebApplicationImpl.java?rev=891246&view=auto
==============================================================================
--- geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebApplicationImpl.java (added)
+++ geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebApplicationImpl.java Wed Dec 16 14:22:04 2009
@@ -0,0 +1,373 @@
+/*
+ * 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.geronimo.osgi.web.extender;
+
+import java.io.FileNotFoundException;
+import java.net.URI;
+import java.net.URL;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.aries.blueprint.container.WebContainerEventDispatcher;
+import org.apache.geronimo.tomcat.TomcatContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An instance of a WAB deployed to an available Web Container
+ * instance.
+ * @version $Rev$, $Date$
+ */
+public class WebApplicationImpl implements WebApplication, Runnable {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(WebApplicationImpl.class);
+
+    // the bundle where the web application resides
+    private final Bundle bundle;
+    // the deployed context path from the bundle headers
+    private final String contextPath;
+    // the extender bundle that's handling this deployment
+    private final Bundle extenderBundle;
+    // the dispatcher service used for handling events.
+    private final WebContainerEventDispatcher eventDispatcher;
+    // our threadpool for asynch dispatch operations
+    private final ScheduledExecutorService executors;
+    private final AtomicBoolean scheduled = new AtomicBoolean();
+    // our servlet context
+    private ServletContext *context;
+    // the container we deploy to
+    private WebContainerService container;
+
+    private static final String CLASSPATH_HEADER = "Bundle-ClassPath";
+    private static final String WEB_INF_CLASSES = "WEB-INF/classes";
+
+    // service properties published when the app deploys
+    private static final String CONTEXT_SYMBOLIC_NAME_PROPERTY = "osgi.web.symbolicname";
+    private static final String CONTEXT_VERSION_PROPERTY = "osgi.web.version";
+    private static final String CONTEXT_CONTEXT_PATH_PROPERTY = "osgi.web.contextpath";
+
+    /**
+     * Construct a WebApplicationImp object to represent a
+     * WAB-resident application.
+     *
+     * @param bundle    The bundle containing the WAB.
+     * @param extenderBundle
+     *                  The RFC 66 container managing the deployment.
+     * @param eventDispatcher
+     *                  The event dispatcher for broadcasting state changes.
+     * @param executors The common thread pool for scheduling asynchronous processing.
+     * @param contextPath
+     *                  The context path from the WAB headers.
+     */
+    public WebApplicationImpl(Bundle bundle, Bundle extenderBundle, WebContainerService container, WebContainerEventDispatcher eventDispatcher, ScheduledExecutorService executors, String contextPath) {
+        this.bundleContext = bundleContext;
+        this.extenderBundle = extenderBundle;
+        this.eventDispatcher = eventDispatcher;
+        this.contextPath = contextPath;
+        this.executors = executors;
+        this.container = container;
+        // create an access control context for performing
+        // registration activities
+        if (System.getSecurityManager() != null) {
+            this.accessControlContext = createAccessControlContext();
+        }
+    }
+
+    /**
+     * Provide access to the bundle where the application resides.
+     *
+     * @return The Bundle instance for the WAB.
+     */
+    public Bundle getBundle() {
+        return bundle;
+    }
+
+
+    /**
+     * Retrieve the extender bundle hosting this application instance.
+     *
+     * @return The extender Bundle instance.
+     */
+    public Bundle getExtenderBundle() {
+        return extenderBundle;
+    }
+
+    /**
+     * Get the event dispatcher instance associated with this
+     * application.
+     *
+     * @return The configured event dispatcher information.
+     */
+    public WebContainerEventDispatcher getEventDispatcher() {
+        return eventDispatcher;
+    }
+
+    /**
+     * Schedule this handler for deployment processing.
+     */
+    public void schedule() {
+        // only one scheduled startup at a time.
+        if (scheduled.compareAndSet(false, true)) {
+            executors.submit(this);
+        }
+    }
+
+    /**
+     * Run the application deployment process in a separate thread.
+     */
+    public void run() {
+        scheduled.set(false);
+        synchronized (scheduled) {
+            doRun();
+        }
+    }
+
+    // private methods for managing event broadcasts.
+    private void deploying() {
+        eventDispatcher.deploying(bundle, contextPath);
+    }
+
+    private void deployed() {
+        eventDispatcher.deployed(bundle, contextPath);
+    }
+
+
+    private void undeploying() {
+        eventDispatcher.undeploying(bundle, contextPath);
+    }
+
+    private void undeployed() {
+        eventDispatcher.undeployed(bundle, contextPath);
+    }
+
+    private void failed(Throwable cause) {
+        eventDispatcher.failed(bundle, contextPath, cause);
+    }
+
+    /**
+     * This method must be called inside a synchronized block to ensure this method is not run concurrently
+     */
+    private void doRun() {
+        try {
+            if (destroyed) {
+                return;
+            }
+            LOGGER.debug("Running web container container for bundle {}", getBundle().getSymbolicName());
+            // send out a broadcast alert that we're going to do this
+            deploying();
+            // validate all of the required elements of the WAB
+            validateMetadata();
+            // ask the container to deploy this
+            container.deploy(this);
+            // register the servlet context
+            registerServletContext();
+            // send out the deployed event
+            deployed();
+        } catch (Throwable t) {
+            state = State.Failed;
+            LOGGER.error("Unable to start web application for bundle " + getBundle().getSymbolicName(), t);
+            // broadcast a failure event
+            failed();
+        }
+    }
+
+
+    /**
+     * Register the ServletContext for this web application
+     * in the service registry
+     */
+    private void registerServletContext() {
+        Properties props = new Properties();
+        props.put(BlueprintConstants.CONTEXT_SYMBOLIC_NAME_PROPERTY, bundleContext.getBundle().getSymbolicName());
+        props.put(BlueprintConstants.CONTEXT_VERSION_PROPERTY, getBundleVersion(bundleContext.getBundle()));
+        props.put(BlueprintConstants.CONTEXT_CONTEXT_PATH_PROPERTY, contextPath);
+        registration = registerService(new String [] { ServletContext.class.getName() }, this.getServletContext(), props);
+    }
+
+
+    /**
+     * unregister the ServletContext for this web application
+     * in the service registry
+     */
+    private void unregisterServletContext() {
+        // unregister the servlet context instance
+        registration.unregister();
+    }
+
+    /**
+     * Register a service using the access control context of
+     * the hosting bundle.
+     *
+     * @param classes    The interface classes for the service.
+     * @param service    The service instance.
+     * @param properties The service properties.
+     *
+     * @return The ServiceRegistration instance for a successful registration.
+     */
+    public ServiceRegistration registerService(final String[] classes, final Object service, final Dictionary properties) {
+        if (accessControlContext == null) {
+            return bundleContext.registerService(classes, service, properties);
+        } else {
+            return AccessController.doPrivileged(new PrivilegedAction<ServiceRegistration>() {
+                public ServiceRegistration run() {
+                    return bundleContext.registerService(classes, service, properties);
+                }
+            }, accessControlContext);
+        }
+    }
+
+    /**
+     * Create an AccessControlContext instance that allows
+     * the web bundle's permission set to be used for extender
+     * operations.
+     *
+     * @return An AccessControlContext that delegates to the BundleContext.
+     */
+    private AccessControlContext createAccessControlContext() {
+        return new AccessControlContext(AccessController.getContext(),
+                new DomainCombiner() {
+                    public ProtectionDomain[] combine(ProtectionDomain[] arg0,
+                                                      ProtectionDomain[] arg1) {
+                        return new ProtectionDomain[] { new ProtectionDomain(null, null) {
+                            public boolean implies(Permission permission) {
+                                return bundle.getBundleContext().hasPermission(permission);
+                            }
+                        }
+                    };
+                }
+        });
+    }
+
+    public AccessControlContext getAccessControlContext() {
+        return accessControlContext;
+    }
+
+    /**
+     * Undeploy a web application.
+     */
+    public void undeploy() {
+        destroyed = true;
+        // send the undeploying event
+        undeploying();
+        // remove this from the servlet container
+        container.undeploy(this);
+        // remove the servlet context
+        unregisterServletContext();
+        // finished with the undeploy operation
+        undeployed();
+        LOGGER.debug("Web container destroyed: {}", this.bundle);
+    }
+
+
+    /**
+     * Set a container-specific context object for this web
+     * application as an opaque object.
+     *
+     * @param context The new context.
+     */
+    public void setContext(Object context) {
+        // this is the main context
+        this.context = context;
+    }
+
+
+    /**
+     * Retrieve the context associated with the application
+     *
+     * @return
+     */
+    public Object getContext() {
+        return context;
+    }
+
+
+    /**
+     * Set the servlet context associated with this web app.  This
+     * will be registered as a service instance.
+     *
+     * @param servletContext
+     *               The target servlet context.
+     */
+    public void setServletContext(ServletContext *servletContext) {
+        this.servletContext = servletContext;
+    }
+
+
+    /**
+     * Request the ServletContext instance associated with this
+     * application.
+     *
+     * @return
+     */
+    public ServletContext getServletContext() {
+        return servletContext;
+    }
+
+
+    /**
+     * Validate the spec-defined constraints on a WAB.  These
+     * constraints are:
+     *
+     * 1)  If the WAB bundle class path specifies WEB-INF/classes, it
+     * must be the first entry in the class path.
+     *
+     * 2)  If the WAB contains a WEB-INF/classes directory, then
+     * that directory must be list as the first entry on the
+     * Bundle-Classpath.
+     */
+    public void validateMetadata() {
+        String classPathHeader = (String) bundle.getHeaders().get("Bundle-ClassPath");
+        // the default header
+        if (classPathHeader == null) {
+            classPathHeader = ".";
+        }
+
+        // split this into the individual path elements
+        String[] pathes = classPathHeader.split(",");
+        int webInfPath = -1;
+        // scan for WEB-INF/classes on the class path.  If there, it must
+        // be the first entry
+        for (int i = 0; i < webInfPath.length; i++) {
+            if (pathes[i].equals(WEB_INF_CLASSES)) {
+                if (i > 0) {
+                    throw new IllegalArgumentException("WEB-INF/classes must be first entry in the bundle classpath");
+                }
+                // mark it as the first item
+                webInfPath = 0;
+                break
+            }
+        }
+
+        // now check if the bundle contains a WEB-INF/classes directory.  If one is there, then
+        // it is required to be the first entry in the bundle classpath.
+        URL webClasses = bundle.getEntry(WEB_INF_CLASSES);
+        if (webClasses != null && webInfPatch != 0) {
+            throw new IllegalArgumentException("WEB-INF/classes must be first entry in the bundle classpath");
+        }
+    }
+}
+

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebApplicationImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebApplicationImpl.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebApplicationImpl.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerEventDispatcher.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerEventDispatcher.java?rev=891246&view=auto
==============================================================================
--- geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerEventDispatcher.java (added)
+++ geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerEventDispatcher.java Wed Dec 16 14:22:04 2009
@@ -0,0 +1,177 @@
+/**
+ * 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.aries.blueprint.container;
+
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.aries.blueprint.utils.JavaUtils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.service.blueprint.container.BlueprintEvent;
+import org.osgi.service.blueprint.container.BlueprintListener;
+import org.osgi.service.blueprint.container.EventConstants;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Support for dispatching Web container lifecycle events to
+ * the EventAdmin service, if it's available.
+ * @version $Rev$, $Date$
+ */
+public class WebContainerEventDispatcher {
+
+    public static final String DEPLOYING = "org/osgi/service/web/DEPLOYING";
+    public static final String DEPLOYED = "org/osgi/service/web/DEPLOYED";
+    public static final String UNDEPLOYING = "org/osgi/service/web/UNDEPLOYING";
+    public static final String UNDEPLOYED = "org/osgi/service/web/UNDEPLOYED";
+    public static final String FAILED = "org/osgi/service/web/FAILED";
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(WebContainerEventDispatcher.class);
+
+    // our service tracker for the EventAdmin service
+    private ServiceTracker tracker;
+    // the extender bundle we're working on behalf of
+    private Bundle extenderBundle;
+
+
+    public WebContainerEventDispatcher(final BundleContext bundleContext) {
+        // this will track the availability of the EventAdmin service when we need to dispatch
+        tracker = new ServiceTracker(context, org.osgi.service.event.EventAdmin, null);
+        tracker.open();
+    }
+
+
+    /**
+     * Dispatch a deploying event
+     *
+     * @param bundle The bundle we're deploying.
+     * @param contextPath
+     *               The context path information from the bundle.
+     */
+    public void deploying(Bundle bundle, String contextPath) {
+        dispatch(DEPLOYING, bundle, contextPath, null);
+    }
+
+
+    /**
+     * Dispatch a deployed event
+     *
+     * @param bundle The bundle we're deploying.
+     * @param contextPath
+     *               The context path information from the bundle.
+     */
+    public void deployed(Bundle bundle, String contextPath) {
+        dispatch(DEPLOYED, bundle, contextPath, null);
+    }
+
+
+    /**
+     * Dispatch an undeploying event
+     *
+     * @param bundle The bundle we're undeploying.
+     * @param contextPath
+     *               The context path information from the bundle.
+     */
+    public void undeploying(Bundle bundle, String contextPath) {
+        dispatch(UNDEPLOYING, bundle, contextPath, null);
+    }
+
+
+    /**
+     * Dispatch an undeployed event
+     *
+     * @param bundle The bundle we're undeploying.
+     * @param contextPath
+     *               The context path information from the bundle.
+     */
+    public void undeployed(Bundle bundle, String contextPath) {
+        dispatch(UNDEPLOYED, bundle, contextPath, null);
+    }
+
+
+    /**
+     * Dispatch a FAILED event
+     *
+     * @param bundle The bundle we're attempting to deploy
+     * @param contextPath
+     *               The context path information from the bundle.
+     */
+    public void failed(Bundle bundle, String contextPath, Throwable cause) {
+        dispatch(FAILREd, bundle, contextPath, cause);
+    }
+
+    /**
+     * Dispatch an event to the appropriate listeners.
+     *
+     * @param topic  The event topic.
+     * @param bundle The bundle hosting the web application.
+     * @param contextPath
+     *               The contextPath information from the bundle.
+     * @param cause  The potential cause information for the exception.
+     */
+    public void dispatch(String topic, Bundle bundle, String contextPath, Throwable cause) {
+        EventAdmin eventAdmin = (EventAdmin) tracker.getService();
+        if (eventAdmin == null) {
+            return;
+        }
+
+        Dictionary<String,Object> props = new Hashtable<String,Object>();
+        props.put(EventConstants.TYPE, event.getType());
+        props.put(EventConstants.EVENT, event);
+        props.put(EventConstants.TIMESTAMP, System.currentTimeMillis());
+        props.put(EventConstants.BUNDLE, bundle);
+        props.put(EventConstants.BUNDLE_SYMBOLICNAME, bundle.getSymbolicName());
+        props.put(EventConstants.BUNDLE_ID, bundle.getBundleId());
+        props.put(EventConstants.BUNDLE_VERSION, getBundleVersion(bundle));
+        props.put("context.path", contextPath);
+        props.put(EventConstants.EXTENDER_BUNDLE, extenderBundle);
+        props.put(EventConstants.EXTENDER_BUNDLE_ID, extenderBundle.getBundleId());
+        props.put(EventConstants.EXTENDER_BUNDLE_SYMBOLICNAME, extenderBundle.getSymbolicName());
+        props.put(EventConstants.EXTENDER_BUNDLE_VERSION, getBundleVersion(extenderBundle));
+        if (cause != null) {
+            props.put(EventConstants.EXCEPTION, cause);
+        }
+        eventAdmin.postEvent(new Event(topic, props));
+    }
+
+    public void destroy() {
+        this.tracker.close();
+    }
+
+    private Version getBundleVersion(Bundle bundle) {
+        Dictionary headers = bundle.getHeaders();
+        String version = (String)headers.get(Constants.BUNDLE_VERSION);
+        return (version != null) ? Version.parseVersion(version) : Version.emptyVerion;
+    }
+}
+

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerEventDispatcher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerEventDispatcher.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerEventDispatcher.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerExtender.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerExtender.java?rev=891246&view=auto
==============================================================================
--- geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerExtender.java (added)
+++ geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerExtender.java Wed Dec 16 14:22:04 2009
@@ -0,0 +1,196 @@
+/**
+ * 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.geronimo.osgi.web.extender;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An extender bundle to manage deployment of Web
+ * Application Bundles (WABs) to the RFC 66 web container.
+ * @version $Rev$, $Date$
+ */
+public class WebContainerExtender implements BundleActivator, SynchronousBundleListener {
+    // the header that identifies a bundle as being a WAB
+    public final static String WEB_CONTEXT_PATH_HEADER = "Web-ContextPath";
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintExtender.class);
+
+    private BundleContext context;
+    private ScheduledExecutorService executors;
+    private Map<Bundle, WebApplicationBundle> wabs;
+    private WebContainerEventDispatcher eventDispatcher;
+
+    /**
+     * Activate the bundle and initialize the extender instance.
+     *
+     * @param context The BundleContext for our bundle.
+     */
+    public void start(BundleContext context) {
+        LOGGER.debug("Starting blueprint extender...");
+
+        this.context = context;
+        eventDispatcher = new WebContainerEventDispatcher(context);
+        executors = Executors.newScheduledThreadPool(3);
+        containers = new HashMap<Bundle, WebApplicationBundle>();
+
+        // start listening for bundle events
+        context.addBundleListener(this);
+        LOGGER.debug("Blueprint extender started");
+    }
+
+
+    /**
+     * Handle initial startup processing once we've been activated
+     * and have web container service that we can deploy to.
+     */
+    protected void handleInitialStartup() {
+        // we need to check all of the active bundles at startup to see if
+        // there are any WABs installed that require startup.
+        Bundle[] bundles = context.getBundles();
+        for (Bundle b : bundles) {
+            // If the bundle is active, check it
+            if (b.getState() == Bundle.ACTIVE) {
+                checkBundle(b);
+            // Also check bundles in the starting state with a lazy activation policy
+            } else if (b.getState() == Bundle.STARTING) {
+                String activationPolicyHeader = (String) b.getHeaders().get(Constants.BUNDLE_ACTIVATIONPOLICY);
+                if (activationPolicyHeader != null && activationPolicyHeader.startsWith(Constants.ACTIVATION_LAZY)) {
+                    checkBundle(b);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Shutdown the extender bundle at termination time.
+     *
+     * @param context Our BundleContext.
+     */
+    public void stop(BundleContext context) {
+        LOGGER.debug("Stopping blueprint extender...");
+        context.removeBundleListener(this);
+        // There's no shutdown ordering of the containers, so just
+        // iterate over the list
+        for (WebApplication wab : wabs) {
+            destroyContext(wab);
+        }
+
+        this.eventDispatcher.destroy();
+        executors.shutdown();
+        LOGGER.debug("Blueprint extender stopped");
+    }
+
+    /**
+     * Handle state changes in the bundles to determine if
+     * it is time to start or destroy an instance.
+     *
+     * @param event  The broadcast event.
+     */
+    public void bundleChanged(BundleEvent event) {
+        Bundle bundle = event.getBundle();
+        // we start processing these upon a lazy activation.
+        if (event.getType() == BundleEvent.LAZY_ACTIVATION) {
+            checkBundle(bundle);
+        } else if (event.getType() == BundleEvent.STARTED) {
+            // the bundle is fully started.  This might be a transition to
+            // the STARTED state for a lazy activated bundle, so only process
+            // this if the bundles not already in our active list.
+            WebApplication wab = wabs.get(bundle);
+            // not seen this one before, go check if it requires processing
+            // and kick off the deployment if required.
+            if (wab == null) {
+                checkBundle(bundle);
+            }
+        } else if (event.getType() == BundleEvent.STOPPING) {
+            // a bundle is stopping.  Check to see if this is one we're managing
+            // and kick off the processing.
+            WebApplication wab = wabs.get(bundle);
+            // If this bundle is one we've processed, handle the undeployment
+            // and shutdown of the bundle.
+            if (wab != null) {
+                destroyContext(wab);
+            }
+        }
+    }
+
+    /**
+     * Destroy a web application deployment, either as a result
+     * of the host bundle getting started, the extended getting stopped,
+     * or the hosting Web Container service going away.
+     *
+     * @param wab    The deployed application.
+     */
+    private void destroyContext(WebApplication wab) {
+        // remove from our global list before destroying
+        Bundle bundle = wab.getBundle();
+
+        wabs.remove(bundle);
+        LOGGER.debug("Destroying ServletContext for bundle {}", bundle.getSymbolicName());
+        // destroy the context
+        wab.destroy();
+        // and broadcast the destroy event
+        eventDispatcher.contextDestroyed(bundle);
+    }
+
+    /**
+     * Check a started bundle to detect if this bundle
+     * should be handled by this extender instance.
+     *
+     * @param bundle The source bundle.
+     */
+    private void checkBundle(Bundle bundle) {
+        LOGGER.debug("Scanning bundle {} for WAB application", bundle.getSymbolicName());
+        try {
+            List<Object> pathList = new ArrayList<Object>();
+            String contextPathHeader = (String) bundle.getHeaders().get(WEB_CONTEXT_PATH_HEADER);
+            // a WAB MUST have the Web-ContextPath header or it must be ignored by the extender.
+            if (contextPathHeader == null) {
+                LOGGER.debug("No web container application found in bundle {}", bundle.getSymbolicName());
+                return;
+            }
+            LOGGER.debug("Found web container application in bundle {} with context path: {}", bundle.getSymbolicName(), contextPathHeader);
+            WebApplicationBundle webApp = new WebApplicationBundle(bundle, context.getBundle(), eventDispatcher, webContainer, executors, contextPathHeader);
+            wabs.put(bundle, webApp);
+            webApp.schedule();
+        } catch (Throwable t) {
+            eventDispatcher.dispatch(new WebContainerEvent(WebContainerEvent.FAILURE, bundle, context.getBundle(), t));
+        }
+    }
+}
+

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerExtender.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerExtender.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/extender/WebContainerExtender.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebAppContext.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebAppContext.java?rev=891246&view=auto
==============================================================================
--- geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebAppContext.java (added)
+++ geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebAppContext.java Wed Dec 16 14:22:04 2009
@@ -0,0 +1,363 @@
+/*
+ * 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.geronimo.osgi.web.extender;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.transaction.UserTransaction;
+
+import org.apache.InstanceManager;
+import org.apache.catalina.Context;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Valve;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.geronimo.connector.outbound.connectiontracking.TrackedConnectionAssociator;
+import org.apache.geronimo.kernel.Kernel;
+import org.apache.geronimo.kernel.osgi.BundleClassLoader;
+import org.apache.geronimo.tomcat.util.SecurityHolder;
+import org.apache.tomcat.TomcatContext;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class TomcatOSGiWebAppContext implements TomcatContext, StatisticsProvider {
+    // the Tomcat-specific deployment descriptor
+    TomcatWebAppType tomcatWebApp;
+    // the working directory for JSP compilation
+    protected File workDir;
+    // a Holder used for annotation processing.  We just
+    // create a default empty one
+    protected final Holder holder;
+    //  used for managing the usage statistics
+    protected ModuleStats statsProvider;
+    protected boolean resetStats = true;
+    // our displayable name, if any
+    protected String displayName;
+    // our virtual server name, if configured
+    protected String virtualServer;
+
+    public TomcatOSGiWebAppContext(Bundle bundle, String contextPath) {
+        super(bundle, contextPath);
+
+        // parse the vendor-specific deployment descriptor
+        tomcatWebApp = getTomcatWebApp();
+
+        // get the root of the framework provided persistence area
+        File base = bundle.getBundleContext().getDataFile("");
+        // create a working directory for this bundle, and attempt to create it.
+        workDir = new File(base, "webApp");
+        workDir.mkdir();
+        // the container requires this, but a default empty one
+        // is sufficient
+        this.holder = new Holder();
+    }
+
+
+    TomcatWebAppType getTomcatWebApp() throws DeploymentException {
+        XmlObject rawPlan = null;
+        try {
+            // load the geronimo-web.xml from either the supplied plan or from the earFile
+            try {
+                // look for Geronimo-specific file first
+                URL path = bundle.getEntry("WEB-INF/geronimo-web.xml");
+                try {
+                    rawPlan = XmlBeansUtil.parse(path, getClass().getClassLoader());
+                } catch (FileNotFoundException e) {
+                    path = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/geronimo-tomcat.xml");
+                    try {
+                        rawPlan = XmlBeansUtil.parse(path, getClass().getClassLoader());
+                    } catch (FileNotFoundException e1) {
+                        log.warn("Web application in bundle " + bundle + " does not contain a WEB-INF/geronimo-web.xml deployment plan.  This may or may not be a problem, depending on whether you have things like resource references that need to be resolved.");
+                    }
+                }
+            } catch (IOException e) {
+                log.warn("Failed to load geronimo-web.xml for bundle " + bundle, e);
+            }
+
+            TomcatWebAppType tomcatWebApp;
+            if (rawPlan != null) {
+                XmlObject webPlan = new GenericToSpecificPlanConverter(GerTomcatDocument.type.getDocumentElementName().getNamespaceURI(),
+                        TomcatWebAppDocument.type.getDocumentElementName().getNamespaceURI(), "tomcat").convertToSpecificPlan(rawPlan);
+                tomcatWebApp = (TomcatWebAppType) webPlan.changeType(TomcatWebAppType.type);
+                XmlBeansUtil.validateDD(tomcatWebApp);
+            } else {
+                // create a default plae
+                tomcatWebApp = TomcatWebAppType.Factory.newInstance();
+            }
+            return tomcatWebApp;
+        } catch (XmlException e) {
+            throw new DeploymentException("xml problem for web app in bundle " + bundle, e);
+        }
+    }
+
+    protected void configureContext() throws DeploymentException {
+//        configureBasicWebModuleAttributes(webApp, tomcatWebApp, moduleContext, earContext, webModule, webModuleData);
+        try {
+            //get Tomcat display-name
+            if (webApp.getDisplayNameArray().length > 0) {
+                displayName = webApp.getDisplayNameArray()[0].getStringValue();
+            }
+
+            // Process the Tomcat container-config elements
+            if (tomcatWebApp.isSetHost()) {
+                virtualServer = tomcatWebApp.getHost().trim();
+            }
+            if (tomcatWebApp.isSetCrossContext()) {
+                crossContext = true;
+            }
+            if (tomcatWebApp.isSetDisableCookies()) {
+                disableCookies = true;
+            }
+
+//            if (tomcatWebApp.isSetTomcatRealm()) {
+//                String tomcatRealm = tomcatWebApp.getTomcatRealm().trim();
+//                AbstractName realmName = earContext.getNaming().createChildName(moduleName, tomcatRealm, RealmGBean.GBEAN_INFO.getJ2eeType());
+//                webModuleData.setReferencePattern("TomcatRealm", realmName);
+//            }
+
+//            if (tomcatWebApp.isSetValveChain()) {
+//                String valveChain = tomcatWebApp.getValveChain().trim();
+//                AbstractName valveName = earContext.getNaming().createChildName(moduleName, valveChain, ValveGBean.J2EE_TYPE);
+//                webModuleData.setReferencePattern("TomcatValveChain", valveName);
+//            }
+
+//            if (tomcatWebApp.isSetListenerChain()) {
+//                String listenerChain = tomcatWebApp.getListenerChain().trim();
+//                AbstractName listenerName = earContext.getNaming().createChildName(moduleName, listenerChain, LifecycleListenerGBean.J2EE_TYPE);
+//                webModuleData.setReferencePattern("LifecycleListenerChain", listenerName);
+//            }
+
+//            if (tomcatWebApp.isSetCluster()) {
+//                String cluster = tomcatWebApp.getCluster().trim();
+//                AbstractName clusterName = earContext.getNaming().createChildName(moduleName, cluster, CatalinaClusterGBean.J2EE_TYPE);
+//                webModuleData.setReferencePattern("Cluster", clusterName);
+//            }
+
+//            if (tomcatWebApp.isSetManager()) {
+//                String manager = tomcatWebApp.getManager().trim();
+//                AbstractName managerName = earContext.getNaming().createChildName(moduleName, manager, ManagerGBean.J2EE_TYPE);
+//                webModuleData.setReferencePattern(TomcatWebAppContext.GBEAN_REF_MANAGER_RETRIEVER, managerName);
+//            }
+
+//            Boolean distributable = webApp.getDistributableArray().length == 1 ? TRUE : FALSE;
+//            if (TRUE == distributable) {
+//                clusteringBuilders.build(tomcatWebApp, earContext, moduleContext);
+//                if (null == webModuleData.getReferencePatterns(TomcatWebAppContext.GBEAN_REF_CLUSTERED_VALVE_RETRIEVER)) {
+//                    log.warn("No clustering builders configured: app will not be clustered");
+//                }
+//            }
+
+
+            if (tomcatWebApp.isSetSecurityRealmName()) {
+                if (earContext.getSecurityConfiguration() == null) {
+                    throw new DeploymentException("You have specified a <security-realm-name> for the web bundle " + bundle + " but no <security> configuration (role mapping) is supplied in the Geronimo plan for the web application");
+                }
+
+                SecurityHolder securityHolder = new SecurityHolder();
+                String securityRealmName = tomcatWebApp.getSecurityRealmName().trim();
+
+                webModuleData.setReferencePattern("RunAsSource", (AbstractNameQuery)earContext.getGeneralData().get(ROLE_MAPPER_DATA_NAME));
+                webModuleData.setReferencePattern("ConfigurationFactory", new AbstractNameQuery(null, Collections.singletonMap("name", securityRealmName), ConfigurationFactory.class.getName()));
+
+                /**
+                 * TODO - go back to commented version when possible.
+                 */
+                String policyContextID = moduleName.toString().replaceAll("[, :]", "_");
+                securityHolder.setPolicyContextID(policyContextID);
+
+                ComponentPermissions componentPermissions = buildSpecSecurityConfig(webApp);
+                earContext.addSecurityContext(policyContextID, componentPermissions);
+                //TODO WTF is this for?
+                securityHolder.setSecurity(true);
+
+                webModuleData.setAttribute("securityHolder", securityHolder);
+                //local jaspic configuration
+                if (tomcatWebApp.isSetAuthentication()) {
+                    AuthenticationWrapper authType = new TomcatAuthenticationWrapper(tomcatWebApp.getAuthentication());
+                    configureLocalJaspicProvider(authType, contextPath, module, webModuleData);
+                }
+
+            }
+        } catch (DeploymentException de) {
+            throw de;
+        } catch (Exception e) {
+            throw new DeploymentException("Unable to initialize GBean for web app " + module.getName(), e);
+        }
+    }
+
+    /**
+     * Retrieve the fully resolved context path for this instance.
+     *
+     * @return The fully resolved context path.
+     */
+    public String getContextPath() {
+        return path;
+    }
+
+    /**
+     * Set the container context for this application.  This is
+     * is set by the container when the app is deployed.
+     *
+     * @param ctx    The new context for this application.
+     */
+    public void setContext(Context ctx) {
+        context = ctx;
+        // set the bundle context attribute in the servlet context
+        context.getServletContainer().setAttribute(ATTRIBUTE_BUNDLE_CONTEXT, bundle.getBundleContext());
+        if (context instanceof StandardContext) {
+            statsProvider =  new ModuleStats((StandardContext)context);
+        }
+    }
+
+    /**
+     * Retrieve the deployment context for this application.
+     *
+     * @return The container-provided deployment context.
+     */
+    public Context getContext() {
+        return context,
+    }
+
+    /**
+     * Return the docbase for this application context.
+     *
+     * @return Always returns "" since we can't portably map this to a file
+     *         location.
+     */
+    public String getDocBase() {
+        return "";        // the bundle is installed, and doesn't really have a docbase location
+    }
+
+    public SecurityHolder getSecurityHolder() {
+        // we don't have a security holder to work about
+        return null;
+    }
+
+    public void String getVirtualServer() {
+        return virtualServer;
+    }
+
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    public UserTransaction getUserTransaction() {
+        return null;
+    }
+
+    public javax.naming.Context getJndiContext() {
+        return null;
+    }
+
+    public Kernel getKernel() {
+        return null;
+    }
+
+    public Set getApplicationManagedSecurityResources() {
+        return null;
+    }
+
+    public TrackedConnectionAssociator getTrackedConnectionAssociator() {
+        return null;
+    }
+
+    public Set getUnshareableResources() {
+        return null;
+    }
+
+    public Realm getRealm() {
+        return null;
+    }
+
+    public Valve getClusteredValve() {
+        return null;
+    }
+
+    public List getValveChain() {
+        return null;
+    }
+
+    public List getLifecycleListenerChain() {
+        return null;
+    }
+
+    public CatalinaCluster getCluster() {
+        return null;
+    }
+
+    public Manager getManager() {
+        return null;
+    }
+
+    public boolean isCrossContext() {
+        return false;
+    }
+
+    public String getWorkDir() {
+        return workDir;
+    }
+
+    public boolean isDisableCookies() {
+        return false;
+    }
+
+    public Map getWebServices() {
+        return null;
+    }
+
+    /**
+     * Create an instance manager for this WAB context.  This
+     * is basically an empty context that's dummied up.
+     *
+     * @return A dummy, empty, InstanceManager instance.
+     */
+    public InstanceManager getInstanceManager() {
+        return new TomcatInstanceManager(holder, classLoader, new ImmutableContext(new HashMap(), false));
+    }
+
+    //  JSR 77 statistics - The static values are initialized at the time of
+    //  creation, getStats return fresh value everytime
+    public Stats getStats() {
+        if (resetStats) {
+            resetStats = false;
+            return statsProvider.getStats();
+        }
+        else {
+            return statsProvider.updateStats();
+        }
+    }
+
+    public void resetStats() {
+        resetStats = true;
+    }
+
+
+    /**
+     * Perform resource cleanup when the servlet context is
+     * destroyed.
+     */
+    public destroy() {
+        statsProvider = null;
+    }
+}

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebAppContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebAppContext.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebAppContext.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebContainer.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebContainer.java?rev=891246&view=auto
==============================================================================
--- geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebContainer.java (added)
+++ geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebContainer.java Wed Dec 16 14:22:04 2009
@@ -0,0 +1,171 @@
+/*
+ * 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.geronimo.osgi.web.extender;
+
+import java.io.FileNotFoundException;
+import java.net.URI;
+import java.net.URL;
+import java.util.Properties;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.aries.blueprint.container.WebContainerEventDispatcher;
+import org.apache.geronimo.tomcat.TomcatContainer;
+import org.apache.geronimo.tomcat.TomcatContext;
+import org.apache.geronimo.tomcat.TomcatWebAppContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A service target for the RFC 66 Web Container extender to
+ * publish an application to the Geronimo-hosted Tomcat
+ * web container instance.
+ * @version $Rev$, $Date$
+ */
+public class TomcatOSGiWebContainer implements WebContainerService, GBeanLifeCycle {
+
+    private static final Logger log = LoggerFactory.getLogger(TomcatOSGiWebContainer.class);
+
+    /**
+     * The attributed used to retrieve the application bundle context.
+     */
+    public static final String ATTRIBUTE_BUNDLE_CONTEXT = "osgi-bundlecontext";
+
+    // the container we deploy to
+    protected final TomcatContainer container;
+    // our bundle context (used for registering services)
+    protected BundleContext context;
+    // our service registration
+    protected ServiceRegistration registration;
+
+    public TomcatOSGiWebContainer (BundleContext bundleContext, TomcatContainer container) throws Exception {
+        assert container != null;
+        this.container = container;
+    }
+
+    /**
+     * Retrieve the real web container instance used for deployment.
+     *
+     * @return The web container instance.
+     */
+    public WebContainer getContainer() {
+        return container;
+    }
+
+
+    /**
+     * Deploy a web application to the OSGi container.
+     *
+     * @param app    The web application instance to deploy, configured as
+     *               a Web Application Bundle.
+     */
+    public void deploy(WebApplication app) {
+        TomcatContext context = new TomcatOSGiWebAppContext(app.getBundle(), app.getContextPath();
+        container.addContext(context);
+        // hook up the application to its context
+        app.setContext(context);
+        // we need to explicitly set the servlet context because the app only gets
+        // an opaque view of the context object.
+        app.setServletContext(context.getContext().getServletContext());
+        //register the classloader <> dir context association so that tomcat's jndi based getResources works.
+        DirContext resources = context.getResources();
+        if (resources == null) {
+            throw new IllegalStateException("JNDI environment was not set up correctly due to previous error");
+        }
+        DirContextURLStreamHandler.bind(classLoader, resources);
+    }
+
+
+    public void undeploy(WebApplication app) {
+        TomcatContext context = (TomcatContext)app.getContext();
+        // remove this from the container
+        container.removeContext(app.context);
+        DirContextURLStreamHandler.unbind(context.getClassLoader());
+
+        log.debug("Tomcat Web App undeployed");
+    }
+
+
+    /**
+     * Start the OSGi Web App container GBean.
+     *
+     * @exception Exception
+     */
+    public void doStart() throws Exception {
+        // register ourselves as a service so that the extender can find us for deployment.
+        registration = bundleContext.registerService(WebContainerService.class.getName(), this, new Properties());
+
+        log.debug("Tomcat OSGi Web App Container started");
+    }
+
+    /**
+     * Stop the web container instance.  This will remove
+     * the service registration from the OSGi registry.
+     *
+     * @exception Exception
+     */
+    public void doStop() throws Exception {
+        registration.unregister();
+        registration = null;
+        log.debug("Tomcat OSGi Web App Container stopped");
+    }
+
+    /**
+     * Handle GBean startup failures
+     */
+    public void doFail() {
+        if (registration != null) {
+            registration.unregister();
+            registration = null;
+        }
+        log.warn("Tomcat Web App Container failed");
+    }
+
+    public static final GBeanInfo GBEAN_INFO;
+    public static final String GBEAN_REF_MANAGER_RETRIEVER = "ManagerRetriever";
+
+    static {
+        GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic("Tomcat OSGi Web Application Container", TomcatOSGiWebContainer.class, NameFactory.WEB_MODULE);
+
+        infoBuilder.addAttribute("bundleContext", BundleContext.class, false);
+        infoBuilder.addReference("Container", TomcatContainer.class, GBeanInfoBuilder.DEFAULT_J2EE_TYPE);
+
+        infoBuilder.addInterface(WebModule.class);
+
+        infoBuilder.setConstructor(new String[] {
+                "bundleContext",
+                "Container",
+                }
+        );
+
+        GBEAN_INFO = infoBuilder.getBeanInfo();
+    }
+
+    public static GBeanInfo getGBeanInfo() {
+        return GBEAN_INFO;
+    }
+}

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebContainer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebContainer.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/rick/rfc66/src/main/java/org/apache/geronimo/osgi/web/tomcat/TomcatOSGiWebContainer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain