All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.geronimo.osgi.web.extender.WebContainerExtender Maven / Gradle / Ivy

The newest version!
/**
 * 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.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.geronimo.gbean.AbstractNameQuery;
import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.gbean.SingleElementCollection;
import org.apache.geronimo.gbean.annotation.GBean;
import org.apache.geronimo.gbean.annotation.ParamAttribute;
import org.apache.geronimo.gbean.annotation.ParamReference;
import org.apache.geronimo.gbean.annotation.ParamSpecial;
import org.apache.geronimo.gbean.annotation.SpecialAttributeType;
import org.apache.geronimo.j2ee.deployment.ModuleBuilder;
import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
import org.apache.geronimo.kernel.Kernel;
import org.apache.geronimo.kernel.config.ConfigurationManager;
import org.apache.geronimo.kernel.config.ConfigurationUtil;
import org.apache.geronimo.kernel.repository.Environment;
import org.apache.geronimo.kernel.repository.Repository;
import org.apache.geronimo.kernel.util.BundleUtil;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.Constants;
import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer;
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: 1213810 $, $Date: 2011-12-14 02:00:08 +0800 (Wed, 14 Dec 2011) $
 */
@GBean
public class WebContainerExtender implements GBeanLifecycle {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebContainerExtender.class);

    private static final String SYNCHRONOUS_HEADER = "Synchronous-Deploy";
    private static final String SYNCHRONOUS_PROPERTY = "org.apache.geronimo.osgi.web.extender.synchronous";
    
    private final Kernel kernel;
    private final BundleContext bundleContext;
    private final Environment defaultEnvironment;
    private final AbstractNameQuery serverName;
    private final AbstractNameQuery transactionManagerObjectName;
    private final AbstractNameQuery connectionTrackerObjectName;
    private final AbstractNameQuery corbaGBeanObjectName;
    private final AbstractNameQuery globalContextAbstractName;
    private final SingleElementCollection webConfigBuilder;
    private final Collection repositories;
    private final ConfigurationManager configurationManager;
    
    private boolean stopped;
    private Map contextPathMap;
    private WebContainerEventDispatcher eventDispatcher;
    private BundleTracker bt;
    private ExecutorService executor;
    
    public WebContainerExtender(@ParamAttribute(name = "defaultEnvironment") Environment defaultEnvironment,
                                @ParamAttribute(name = "transactionManagerAbstractName") AbstractNameQuery transactionManagerAbstractName,
                                @ParamAttribute(name = "connectionTrackerAbstractName") AbstractNameQuery connectionTrackerAbstractName,
                                @ParamAttribute(name = "corbaGBeanAbstractName") AbstractNameQuery corbaGBeanAbstractName,
                                @ParamAttribute(name = "globalContextAbstractName") AbstractNameQuery globalContextAbstractName,
                                @ParamAttribute(name = "serverName") AbstractNameQuery serverName,
                                @ParamReference(name = "Repositories", namingType = "Repository") Collection repositories,
                                @ParamReference(name = "WebModuleBuilders", namingType = NameFactory.MODULE_BUILDER) Collection webModuleBuilders,
                                @ParamSpecial(type = SpecialAttributeType.kernel) Kernel kernel,
                                @ParamSpecial(type = SpecialAttributeType.bundleContext) BundleContext bundleContext) throws Exception {
        this.defaultEnvironment = defaultEnvironment;
        this.transactionManagerObjectName = transactionManagerAbstractName;
        this.connectionTrackerObjectName = connectionTrackerAbstractName;
        this.corbaGBeanObjectName = corbaGBeanAbstractName;
        this.globalContextAbstractName = globalContextAbstractName;
        this.serverName = serverName;
        this.repositories = repositories;
        this.webConfigBuilder = new SingleElementCollection(webModuleBuilders);
        this.bundleContext = bundleContext;
        this.kernel = kernel;   
        this.configurationManager = ConfigurationUtil.getConfigurationManager(kernel);
    }

    AbstractNameQuery getServerName() {
        return serverName;
    }

    AbstractNameQuery getTransactionManagerObjectName() {
        return transactionManagerObjectName;
    }

    AbstractNameQuery getConnectionTrackerObjectName() {
        return connectionTrackerObjectName;
    }

    AbstractNameQuery getCorbaGBeanObjectName() {
        return corbaGBeanObjectName;
    }

    AbstractNameQuery getGlobalContextAbstractName() {
        return globalContextAbstractName;
    }
    
    Collection getRepositories() {
        return repositories;
    }
    
    Kernel getKernel() {
        return kernel;
    }
    
    ConfigurationManager getConfigurationManager() {
        return configurationManager;
    }
    
    ModuleBuilder getWebModuleBuilder() {
        return webConfigBuilder.getElement();
    }
    
    /**
     * Activate the bundle and initialize the extender instance.
     *
     * @param context The BundleContext for our bundle.
     */
    public void start(BundleContext context) {
        LOGGER.debug("Starting web container extender...");

        stopped = false;
        executor = Executors.newFixedThreadPool(3);
        contextPathMap = Collections.synchronizedMap(new HashMap());  
        eventDispatcher = new WebContainerEventDispatcher(context);
        bt = new BundleTracker(context, Bundle.STARTING | Bundle.ACTIVE, new WebBundleTrackerCustomizer());
        bt.open();
        
        LOGGER.debug("Web container extender started");
    }

    public ExecutorService getExecutorService() {
        return executor;
    }
    
    /**
     * Get the event dispatcher instance associated with this
     * application.
     *
     * @return The configured event dispatcher information.
     */
    public WebContainerEventDispatcher getEventDispatcher() {
        return eventDispatcher;
    }   
    
    private class WebBundleTrackerCustomizer implements BundleTrackerCustomizer {

        public Object addingBundle(Bundle bundle, BundleEvent event) {
            if (bundle.getState() == Bundle.ACTIVE) {
                return deploy(bundle);
            } else if (bundle.getState() == Bundle.STARTING) {
                String activationPolicyHeader = (String) bundle.getHeaders().get(Constants.BUNDLE_ACTIVATIONPOLICY);
                if (activationPolicyHeader != null && activationPolicyHeader.startsWith(Constants.ACTIVATION_LAZY)) {
                    return deploy(bundle);
                }
            }
            return null;
        }

        public void modifiedBundle(Bundle bundle, BundleEvent event, Object arg) {
        }

        public void removedBundle(Bundle bundle, BundleEvent event, Object arg) {
            undeploy((WebApplication) arg);
        }
        
    }

    /**
     * Shutdown the extender bundle at termination time.
     *
     * @param context Our BundleContext.
     */
    public void stop(BundleContext context) {
        LOGGER.debug("Stopping web container extender...");
        
        stopped = true;
        if (bt != null) {
            bt.close();
        }
        if (eventDispatcher != null) {
            eventDispatcher.destroy();
        }
        if (executor != null) {
            executor.shutdown();
        }
        
        LOGGER.debug("Web container extender stopped");
    }

    /**
     * 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 undeploy(WebApplication wab) {
        WebApplications webApplications = contextPathMap.get(wab.getContextPath());
        if (wab == webApplications.getDeployed()) {
            wab.undeploy();
        } else {
            webApplications.remove(wab);
        }
    }

    /**
     * Check a started bundle to detect if this bundle
     * should be handled by this extender instance.
     *
     * @param bundle The source bundle.
     */
    private WebApplication deploy(Bundle bundle) {
        LOGGER.debug("Scanning bundle {} for WAB application", bundle.getSymbolicName());
        String contextPath = (String) bundle.getHeaders().get(BundleUtil.WEB_CONTEXT_PATH_HEADER);
        // a WAB MUST have the Web-ContextPath header or it must be ignored by the extender.
        if (contextPath == null) {
            LOGGER.debug("No web container application found in bundle {}", bundle.getSymbolicName());
            return null;
        }
        LOGGER.debug("Found web container application in bundle {} with context path: {}", bundle.getSymbolicName(), contextPath);
        WebApplications webApplications = getWebApplications(contextPath);
        WebApplication webApp = new WebApplication(this, bundle, contextPath);
        Collection collisions = webApplications.register(webApp);
        if (!collisions.isEmpty()) {
            eventDispatcher.deploying(bundle, contextPath);
            eventDispatcher.collision(bundle, contextPath, collisions);
            LOGGER.warn("WAB {} cannot be deployed. WAB {} is already deployed with {} Context-Path.", 
                        new Object[] {bundle, webApplications.getDeployed().getBundle(), contextPath});
        }
        return webApp;
    }
        
    private WebApplications getWebApplications(String contextPath) {
        synchronized (contextPathMap) {
            WebApplications webApplications = contextPathMap.get(contextPath);
            if (webApplications == null) {
                webApplications = new WebApplications(contextPath);
                contextPathMap.put(contextPath, webApplications);
            }
            return webApplications;            
        }
    }
 
    protected void unregisterWebApplication(WebApplication wab) {
        WebApplications webApplications = contextPathMap.get(wab.getContextPath());
        webApplications.unregister(wab, !stopped);
    }

    protected void start() {
        start(bundleContext);
    }
    
    protected void stop() {
        stop(bundleContext);        
    }
    
    public void doStart() throws Exception {
    }

    public void doFail() {
    }
    
    public void doStop() throws Exception {
        stop();
    }
    
    private static class WebApplications {
        
        private String contextPath;
        private WebApplication deployed;
        private List waiting;
        
        public WebApplications(String contextPath) {
            this.contextPath = contextPath;
            this.waiting = new LinkedList();
        }
        
        private boolean isSynchronous(Bundle bundle) {
            String property = (String) bundle.getHeaders().get(SYNCHRONOUS_HEADER);           
            if (property == null) {
                property = bundle.getBundleContext().getProperty(SYNCHRONOUS_PROPERTY);
            }
            return Boolean.parseBoolean(property);
        }
        
        public synchronized Collection register(WebApplication webApp) {
            if (deployed == null) {
                deployed = webApp;
                Bundle bundle = webApp.getBundle();
                if (isSynchronous(bundle)) {
                    LOGGER.debug("Deploying web application bundle {} synchronously.", bundle.getSymbolicName());
                    deployed.run();
                } else {
                    LOGGER.debug("Deploying web application bundle {} asynchronously.", bundle.getSymbolicName());
                    deployed.schedule();
                }
                return Collections.emptyList();
            } else {
                waiting.add(webApp);
                List bundleIds = new ArrayList();
                bundleIds.add(deployed.getBundle().getBundleId());
                for (WebApplication app : waiting) {
                    bundleIds.add(app.getBundle().getBundleId());
                }
                return bundleIds;
            }
        }
        
        public synchronized void unregister(WebApplication webApp, boolean redeploy) {
            if (deployed == webApp) {
                deployed = null;
                if (redeploy) {
                    WebApplication candidate = null;
                    for (WebApplication app : waiting) {
                        if (candidate == null || candidate.getBundle().getBundleId() > app.getBundle().getBundleId()) {
                            candidate = app;
                        }
                    }
                    if (candidate != null) {
                        waiting.remove(candidate);
                        deployed = candidate;
                        deployed.schedule();
                    }
                }
            } else {
                waiting.remove(webApp);
            }
        }
        
        public synchronized void remove(WebApplication webApp) {
            waiting.remove(webApp);
        }
        
        public synchronized WebApplication getDeployed() {
            return deployed;
        }
        
        public String toString() {
            return "ContextPath[" + contextPath + " " + deployed + " " + waiting + "]";
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy