Package org.apache.geronimo.aries

Source Code of org.apache.geronimo.aries.ApplicationGBean

/**
*  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.aries;

import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.aries.application.DeploymentContent;
import org.apache.aries.application.DeploymentMetadata;
import org.apache.aries.application.DeploymentMetadataFactory;
import org.apache.aries.application.management.AriesApplication;
import org.apache.aries.application.management.AriesApplicationContext.ApplicationState;
import org.apache.aries.application.management.AriesApplicationContextManager;
import org.apache.aries.application.management.AriesApplicationResolver;
import org.apache.aries.application.management.BundleInfo;
import org.apache.aries.application.management.ManagementException;
import org.apache.aries.application.management.ResolverException;
import org.apache.geronimo.gbean.GBeanLifecycle;
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.kernel.Kernel;
import org.apache.geronimo.kernel.repository.Artifact;
import org.apache.geronimo.osgi.web.WebApplicationUtils;
import org.apache.xbean.osgi.bundle.util.BundleUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.framework.wiring.FrameworkWiring;
import org.osgi.service.packageadmin.PackageAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @version $Rev:385232 $ $Date: 2013-04-03 03:37:31 +0800 (Wed, 03 Apr 2013) $
*/
@GBean
public class ApplicationGBean implements GBeanLifecycle {
       
    private static final Logger LOG = LoggerFactory.getLogger(ApplicationGBean.class);
   
    private static final long applicationStartTimeout = getApplicationStartTimeout();
   
    private final Bundle bundle;
    private final ApplicationInstaller installer;
    private final Artifact configId;
    private final ApplicationUpdateHelper updateHelper;
    private GeronimoApplication application;
    private ApplicationState applicationState;
    private Set<Bundle> applicationBundles;
   
    public ApplicationGBean(@ParamSpecial(type = SpecialAttributeType.kernel) Kernel kernel,
                            @ParamSpecial(type = SpecialAttributeType.bundle) Bundle bundle,
                            @ParamAttribute(name="configId") Artifact configId,
                            @ParamAttribute(name="location") File inPlaceLocation,
                            @ParamReference(name="Installer") ApplicationInstaller installer)
        throws Exception {
        this.bundle = bundle;
        this.installer = installer;
        this.configId = configId;
        this.updateHelper = new ApplicationUpdateHelper(this);

        GeronimoApplicationManager appManager = installer.getGeronimoApplicationManager();
               
        if (inPlaceLocation == null) {
            this.application = appManager.loadApplication(bundle);
        } else {
            this.application = appManager.loadApplication(inPlaceLocation);
        }
           
        DeploymentMetadataFactory deploymentFactory = appManager.getDeploymentMetadataFactory();
        install(deploymentFactory);
                       
        BundleContext bundleContext = bundle.getBundleContext();
        ServiceReference applicationManagerReference =
            bundleContext.getServiceReference(AriesApplicationContextManager.class.getName());
       
        GeronimoApplicationContextManager applicationManager =
            getService(applicationManagerReference, GeronimoApplicationContextManager.class);
        try {
            applicationManager.registerApplicationContext(new GeronimoApplicationContext(this));
        } finally {
            bundleContext.ungetService(applicationManagerReference);
        }
       
        setApplicationState(ApplicationState.INSTALLED);
    }

    public long[] getApplicationContentBundleIds() {
        long[] ids = new long[applicationBundles.size()];
        int i = 0;
        for (Bundle content : applicationBundles) {
            ids[i++] = content.getBundleId();
        }
        return ids;
    }
   
    public long getApplicationContentBundleId(String symbolicName, String version) {
        Bundle bundle = findBundle(symbolicName, version);
        return (bundle == null) ? -1 : bundle.getBundleId();
    }
   
    public File getApplicationContentBundlePublishLocation(String symbolicName, String version) {
        Bundle bundle = findBundle(symbolicName, version);
        if (bundle == null) {
            return null;
        }
        File file = BundleUtils.toFile(bundle);
        return (file != null && file.isDirectory()) ? file : null;
    }

    private Bundle findBundle(String symbolicName, String version) {
        if (symbolicName != null) {
            for (Bundle content : applicationBundles) {
                if (symbolicName.equals(content.getSymbolicName())) {
                    if (version == null) {
                        // if no version specified, return first one that matches
                        return content;
                    } else if (version.equals(content.getVersion().toString())) {
                        // version specified - match exactly
                        return content;
                    }
                }
            }
        }
        return null;
    }
   
    private Bundle findBundle(long bundleId) {
        for (Bundle content : applicationBundles) {
            if (content.getBundleId() == bundleId) {
                return content;
            }
        }
        return null;
    }
   
    public String getApplicationContentBundleSymbolicName(long bundleId) {
        Bundle bundle = findBundle(bundleId);
        return (bundle != null) ? bundle.getSymbolicName() : null;
    }
   
    /*
     * Update contents of a single bundle within the OSGi application.
     *
     * @param bundleId id of the bundle to update.
     * @param file new contents of the bundle.
     */
    public synchronized void updateApplicationContent(long bundleId, File file) throws Exception {
        Bundle targetBundle = findBundle(bundleId);
        if (targetBundle == null) {
            throw new IllegalArgumentException("Could not find bundle with id " + bundleId + " in the application");
        }
        waitForStart();
        updateHelper.updateBundle(targetBundle, file);
        waitForStart();
    }
   
    /*
     * Attempts to hot swap classes of a single bundle within the OSGi application.
     *
     * @param bundleId id of the bundle to update.
     * @param changesFile file containing updated class files for the bundle.
     * @param updateArchive indicates if the application archive file should be updated with the changes.
     */
    public synchronized boolean hotSwapApplicationContent(long bundleId, File changesFile, boolean updateArchive) throws Exception {
        Bundle targetBundle = findBundle(bundleId);
        if (targetBundle == null) {
            throw new IllegalArgumentException("Could not find bundle with id " + bundleId + " in the application");
        }
        waitForStart();
        return updateHelper.updateBundleClasses(targetBundle, changesFile, updateArchive);
    }
  
    /*
     * Update application archive with new bundle contents.
     *
     * @param bundleId id of the bundle to update.
     * @param file file containing new contents of the bundle. The file can contain partial or full contents.
     * @param partial true if file contains partial contents. False, otherwise.
     */
    public synchronized boolean updateApplicationArchive(long bundleId, File file, boolean partial) throws Exception {
        Bundle targetBundle = findBundle(bundleId);
        if (targetBundle == null) {
            throw new IllegalArgumentException("Could not find bundle with id " + bundleId + " in the application");
        }
        try {
            if (partial) {
                updateHelper.updateEBAPartial(targetBundle, file);
            } else {
                updateHelper.updateEBA(targetBundle, file);
            }
            return true;
        } catch (Exception e) {
            LOG.warn("Error updating application archive with the new contents. " +
                     "Changes made might be gone next time the application or server is restarted.", e);
            return false;
        }
    }
       
    protected File getApplicationArchive() throws IOException {
        File ebaArchive = installer.getApplicationLocation(configId);
        if (ebaArchive == null || !ebaArchive.exists()) {
            throw new IOException("Cannot locate application archive for " + configId);
        }
        return ebaArchive;
    }
   
    protected Bundle getBundle() {
        return bundle;
    }
   
    protected AriesApplication getAriesApplication() {
        return application;
    }
   
    protected Set<Bundle> getApplicationContent() {
        return new HashSet<Bundle>(applicationBundles);
    }

    protected ApplicationState getApplicationState() {
        return applicationState;
    }
   
    protected String getApplicationName() {
        return application.getApplicationMetadata().getApplicationScope();
    }
   
    private DeploymentMetadata getDeploymentMetadata(AriesApplicationResolver resolver, DeploymentMetadataFactory deploymentFactory) throws ResolverException {
        DeploymentMetadata meta = application.getDeploymentMetadata();
        if (meta == null) {
            // try to resolve the application
            LOG.debug("Resolving {} application.", getApplicationName());
            Set<BundleInfo> requiredBundles = resolver.resolve(application);
            meta = deploymentFactory.createDeploymentMetadata(application, requiredBundles);
            LOG.debug("Resolved application bundles: {} ", requiredBundles);
        } else {
            LOG.debug("Application {} is resolved.", getApplicationName());
        }
        return meta;
    }
   
    private void install(DeploymentMetadataFactory deploymentFactory) throws Exception {

        BundleContext bundleContext = bundle.getBundleContext();
       
        AriesApplicationResolver resolver = null;
        PackageAdmin packageAdmin = null;
       
        ServiceReference resolverRef = bundleContext.getServiceReference(AriesApplicationResolver.class.getName());
        ServiceReference packageAdminRef = bundleContext.getServiceReference(PackageAdmin.class.getName());
                             
        applicationBundles = new LinkedHashSet<Bundle>();
        try {
            resolver = getService(resolverRef, AriesApplicationResolver.class);
           
            DeploymentMetadata meta = getDeploymentMetadata(resolver, deploymentFactory);
           
            List<DeploymentContent> deploymentContentsBundles = meta.getApplicationDeploymentContents();
            List<DeploymentContent> provisionBundles = meta.getApplicationProvisionBundles();
           
            List<DeploymentContent> bundlesToInstall = new ArrayList<DeploymentContent>();
            bundlesToInstall.addAll(deploymentContentsBundles);
            bundlesToInstall.addAll(provisionBundles);
           
            packageAdmin = getService(packageAdminRef, PackageAdmin.class);
           
            for (DeploymentContent content : bundlesToInstall) {
                String bundleSymbolicName = content.getContentName();
                Version bundleVersion = content.getExactVersion();

                // Step 1: See if bundle is already installed in the framework
                Bundle contentBundle = findBundleInFramework(packageAdmin, bundleSymbolicName, bundleVersion);
                if (contentBundle != null) {
                    // If the contentBundle has been already installed, and the contentBundle is deployment content bundle, it will be added to our application Bundles.
                    // That is, an installed provision bundle is not considered as an application bundle.
                    for (DeploymentContent deploymentContent : deploymentContentsBundles) {
                        if (deploymentContent.getContentName().equals(bundleSymbolicName) && deploymentContent.getExactVersion().equals(bundleVersion)){
                            applicationBundles.add(contentBundle);
                        }
                    }
                   
                } else {
                    // Step 2: See if the bundle is included in the application
                    BundleInfo bundleInfo = findBundleInfoInApplication(bundleSymbolicName, bundleVersion);
                    if (bundleInfo == null) {
                        // Step 3: Lookup bundle location using the resolver
                        bundleInfo = findBundleInfoUsingResolver(resolver, bundleSymbolicName, bundleVersion);
                    }
                   
                    if (bundleInfo == null) {
                        throw new ManagementException("Could not find bundle: " + bundleSymbolicName + "_" + bundleVersion);
                    }
                       
                    contentBundle = bundleContext.installBundle(bundleInfo.getLocation());
                   
                    applicationBundles.add(contentBundle);
                }
               
            }
        } catch (BundleException be) {
            for (Bundle bundle : applicationBundles) {
                bundle.uninstall();
            }

            applicationBundles.clear();

            throw be;
        } finally {
            if (resolver != null) {
                bundleContext.ungetService(resolverRef);
            }
            if (packageAdmin != null) {
                bundleContext.ungetService(packageAdminRef);
            }
        }
    }
   
    @SuppressWarnings("deprecation")
    private Bundle findBundleInFramework(PackageAdmin admin, String symbolicName, Version version) {
        String exactVersion = "[" + version + "," + version + "]";
        Bundle[] bundles = admin.getBundles(symbolicName, exactVersion);
        if (bundles != null && bundles.length == 1) {
            return bundles[0];
        } else {
            return null;
        }
    }
   
    private BundleInfo findBundleInfoInApplication(String symbolicName, Version version) {
        for (BundleInfo info : application.getBundleInfo()) {
            if (info.getSymbolicName().equals(symbolicName)
                && info.getVersion().equals(version)) {
                return info;
            }
        }
        return null;
    }
   
    private BundleInfo findBundleInfoUsingResolver(AriesApplicationResolver resolver, String symbolicName, Version version) {
        return resolver.getBundleInfo(symbolicName, version);
    }
   
    private <T> T getService(ServiceReference ref, Class<T> type) throws ManagementException {
        Object service = null;
        if (ref != null) {
            service = bundle.getBundleContext().getService(ref);
        }
       
        if (service == null) {
            throw new ManagementException(new ServiceException(type.getName(), ServiceException.UNREGISTERED));
        }
       
        return type.cast(service);
    }
   
    private static boolean getFailOnStartError() {
        String property = System.getProperty("org.apache.geronimo.aries.failApplicationOnStartError", "false");
        return Boolean.parseBoolean(property);
    }
   
    public void doStart() throws Exception {
        LOG.debug("Starting {} application.", getApplicationName());
       
        try {
            startApplicationBundles();
            LOG.debug("Application {} started successfully.", getApplicationName());
        } catch (BundleException be) {
            Exception rootException = be;
            String rootMessage = be.getMessage();
           
            // check for resolver errors
            BundleContext bundleContext = bundle.getBundleContext();
            if (bundleContext != null) {
                try {
                    ResolverErrorAnalyzer errorAnalyzer = new ResolverErrorAnalyzer(bundleContext);
                    String resolverErrors = errorAnalyzer.getErrorsAsString(applicationBundles);
                    if (resolverErrors != null) {
                        rootException = null;
                        rootMessage = resolverErrors;
                    }
                } catch (IllegalStateException e) {
                    // bundle is no loger valid, use the original exception
                }
            }

            String message = MessageFormat.format("Error starting {0} application. {1}", getApplicationName(), rootMessage);
           
            if (getFailOnStartError()) {
                throw new BundleException(message, rootException);
            } else {
                LOG.error(message, rootException);
            }
        }       
    }   
   
    private void startApplicationBundles() throws Exception {
        BundleContext context = bundle.getBundleContext();
        FrameworkWiring wiring = context.getBundle(0).adapt(FrameworkWiring.class);
        if (!wiring.resolveBundles(applicationBundles)) {
            LOG.debug("First resolution of application bundles failed.");
            if (!wiring.resolveBundles(applicationBundles)) {
                throw new BundleException("One or more bundles in " + getApplicationName() + " application could not be resolved.");
            } else {
                LOG.debug("Second resolution of application bundles was successful.");
            }
        }

        setApplicationState(ApplicationState.RESOLVED);
        setApplicationState(ApplicationState.STARTING);
       
        LinkedList<Bundle> bundlesWeStarted = new LinkedList<Bundle>();
        try {
            applicationBundles = getSortedBundles();
            for (Bundle b : applicationBundles) {
                if (BundleUtils.canStart(b)) {
                    LOG.debug("Starting {} application bundle.", b);
                    b.start(Bundle.START_TRANSIENT);                
                    bundlesWeStarted.addFirst(b);
                }
            }
            setApplicationState(ApplicationState.ACTIVE);
        } catch (BundleException be) {
            setApplicationState(ApplicationState.STOPPING);
            for (Bundle b : bundlesWeStarted) {
                try {
                    b.stop();
                } catch (BundleException be2) {
                    // we are doing tidyup here, so we don't want to replace the
                    // bundle exception
                    // that occurred during start with one from stop. We also
                    // want to try to stop
                    // all the bundles we started even if some bundles wouldn't
                    // stop.
                }
            }
            setApplicationState(ApplicationState.RESOLVED);
            throw be;
        }
    }

    /*
     * Sorts bundles in bundle dependency order (i.e. Import-Package, Require-Bundle order).
     */
    private LinkedHashSet<Bundle> getSortedBundles() {
        LinkedList<Bundle> stack = new LinkedList<Bundle>();
        LinkedHashSet<Bundle> orderedBundles = new LinkedHashSet<Bundle>(applicationBundles.size());
        for (Bundle bundle : applicationBundles) {
            sortDependentBundles(bundle, orderedBundles, stack);
        }
        return orderedBundles;
    }
   
    private void sortDependentBundles(Bundle bundle, LinkedHashSet<Bundle> sortedBundles, LinkedList<Bundle> stack) {
        if (sortedBundles.contains(bundle)) {
            return;
        }
       
        if (stack.contains(bundle)) {
            // dependency loop detected
            LOG.debug("Bundle dependency loop detected: {}", stack.subList(stack.indexOf(bundle), stack.size()));
            return;
        }
        stack.add(bundle);
       
        BundleWiring wiring = bundle.adapt(BundleWiring.class);
        List<BundleWire> wires;
        wires = wiring.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
        for (BundleWire wire : wires) {
            Bundle wiredBundle = wire.getProviderWiring().getBundle();
            // examine wires to other application bundles only
            if (wiredBundle != bundle && applicationBundles.contains(wiredBundle)) {
                sortDependentBundles(wiredBundle, sortedBundles, stack);
            }
        }
        wires = wiring.getRequiredWires(BundleRevision.BUNDLE_NAMESPACE);
        for (BundleWire wire : wires) {
            Bundle wiredBundle = wire.getProviderWiring().getBundle();
            // examine wires to other application bundles only
            if (wiredBundle != bundle && applicationBundles.contains(wiredBundle)) {
                sortDependentBundles(wiredBundle, sortedBundles, stack);
            }
        }
       
        stack.removeLast();
       
        sortedBundles.add(bundle);
    }
   
    private static long getApplicationStartTimeout() {
        String property = System.getProperty("org.apache.geronimo.aries.applicationStartTimeout", String.valueOf(5 * 60 * 1000));
        return Long.parseLong(property);
    }
   
    private void waitForStart() {
        waitForStart(applicationStartTimeout);
    }
   
    private void waitForStart(long timeout) {
        Set<Bundle> webBundles = new HashSet<Bundle>();
        for (Bundle b : applicationBundles) {
            if (b.getState() == Bundle.ACTIVE && WebApplicationUtils.isWebApplicationBundle(b)) {
                webBundles.add(b);              
            }
        }
        if (!webBundles.isEmpty()) {
            LOG.debug("Waiting {}ms for asynchronous processing for {} bundles to finish", timeout, webBundles);
            try {
                boolean completed = installer.getWebApplicationTracker().waitForBundles(webBundles, timeout);
                if (completed) {
                    LOG.debug("Asynchronous processing completed.");                   
                } else {
                    LOG.debug("Time out while waiting for asynchronous processing to finish.");
                }
            } catch (InterruptedException e) {
                // ignore
            }
        }  
    }
   
    public void doStop() {
        LOG.debug("Stopping {} application.", getApplicationName());
       
        if (getApplicationState() == ApplicationState.ACTIVE) {
            setApplicationState(ApplicationState.STOPPING);
        }
       
        // stop bundles in reverse order
        LinkedList<Bundle> sortedList = new LinkedList<Bundle>();
        for (Bundle bundle : applicationBundles) {
            sortedList.addFirst(bundle);
        }
       
        for (Bundle bundle : sortedList) {
            try {
                LOG.debug("Stopping and uninstalling {} application bundle.", bundle);
                bundle.uninstall();
            } catch (Exception e) {
                LOG.error("Fail to uninstall", e);
            }
        }
        applicationBundles.clear();

        setApplicationState(ApplicationState.UNINSTALLED);       
    }

    public void doFail() {
        doStop();
    }
  
    protected void applicationStart() throws BundleException {
        try {
            installer.getConfigurationManager().loadConfiguration(configId);
            installer.getConfigurationManager().startConfiguration(configId);
        } catch (Exception e) {
            throw new BundleException("Failed to start application", e);           
        }
    }
   
    protected void applicationStop() throws BundleException {
        try {
            installer.getConfigurationManager().unloadConfiguration(configId);
        } catch (Exception e) {
            throw new BundleException("Failed to start application", e);           
        }
    }
   
    protected void applicationUninstall() {
        LOG.debug("Uninstalling {} application.", getApplicationName());

        try {
            installer.getConfigurationManager().unloadConfiguration(configId);
            installer.getConfigurationManager().uninstallConfiguration(configId);
        } catch (Exception e) {
            // ignore
        }
                    
        setApplicationState(ApplicationState.UNINSTALLED);
    }

    private void setApplicationState(ApplicationState newState) {
        if (applicationState != newState) {
            applicationState = newState;
            installer.fireApplicationEvent(application, newState);
        }
    }
       
}
TOP

Related Classes of org.apache.geronimo.aries.ApplicationGBean

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.