Index: module/osgi-javaee-base/src/main/java/org/glassfish/osgijavaeebase/OSGiDeploymentRequest.java =================================================================== --- module/osgi-javaee-base/src/main/java/org/glassfish/osgijavaeebase/OSGiDeploymentRequest.java (revision 51809) +++ module/osgi-javaee-base/src/main/java/org/glassfish/osgijavaeebase/OSGiDeploymentRequest.java (working copy) @@ -2,6 +2,11 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2009-2010 Oracle and/or its affiliates. All rights reserved. + * + * Portions Copyright 2011 ancoron + * + * Ancoron elects to include this software in this distribution under the + * GPL Version 2 license. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -19,7 +24,7 @@ * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. - * + * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: @@ -40,6 +45,9 @@ package org.glassfish.osgijavaeebase; +import com.sun.enterprise.config.serverbeans.Application; +import com.sun.enterprise.config.serverbeans.Applications; +import com.sun.enterprise.config.serverbeans.ServerTags; import com.sun.enterprise.deploy.shared.ArchiveFactory; import com.sun.enterprise.util.io.FileUtils; import org.glassfish.api.ActionReport; @@ -48,14 +56,18 @@ import org.glassfish.api.deployment.OpsParams; import org.glassfish.api.deployment.archive.ReadableArchive; import org.glassfish.api.deployment.archive.WritableArchive; +import org.glassfish.deployment.common.DeploymentUtils; import org.glassfish.internal.api.Globals; import org.glassfish.internal.data.ApplicationInfo; import org.glassfish.internal.deployment.Deployment; import org.glassfish.server.ServerEnvironmentImpl; +import org.jvnet.hk2.config.Transaction; +import org.jvnet.hk2.config.TransactionFailure; import org.osgi.framework.Bundle; import java.io.File; import java.io.IOException; +import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; @@ -79,6 +91,7 @@ private ReadableArchive archive; private OSGiDeploymentContext dc; private OSGiApplicationInfo result; + private String reuseAppEntry = null; public OSGiDeploymentRequest(Deployment deployer, ArchiveFactory archiveFactory, @@ -146,7 +159,19 @@ // 3. Finally deploy and store the result in our inmemory map. archive = makeArchive(); + + String appName = b.getSymbolicName(); + logger.logp(Level.WARNING, "OSGiDeploymentRequest", "prepare", + "[{0}] Searching for an existing application named ''{1}''", + new Object[]{String.valueOf(b.getBundleId()), appName}); + + // first try to find an existing application for this bundle... + if(deployer.get(appName) != null) { + // found one - assuming it is related and valid... + reuseAppEntry = appName; + } + // Set up a deployment context OpsParams opsParams = getDeployParams(); @@ -160,6 +185,35 @@ opsParams, env, b); + + if(reuseAppEntry != null) { + logger.logp(Level.WARNING, "OSGiDeploymentRequest", "prepare", + "[{0}] Trying to reuse existing application named ''{1}''", + new Object[]{String.valueOf(b.getBundleId()), reuseAppEntry}); + + return; + } + + logger.logp(Level.WARNING, "OSGiDeploymentRequest", "prepare", + "[{0}] Preparing new application ...", + new Object[]{String.valueOf(b.getBundleId())}); + + Properties appProps = dc.getAppProps(); + + /* + * If the app's location is within the domain's directory then + * express it in the config as ${com.sun.aas.instanceRootURI}/rest-of-path + * so users can relocate the entire installation without having + * to modify the app locations. Leave the location alone if + * it does not fall within the domain directory. + */ + String appLocation = DeploymentUtils.relativizeWithinDomainIfPossible(archive.getURI()); + + appProps.setProperty(ServerTags.LOCATION, appLocation); + // set to default "user", deployers can override it + // during processing + appProps.setProperty(ServerTags.OBJECT_TYPE, "user"); + appProps.setProperty(ServerTags.DIRECTORY_DEPLOYED, String.valueOf(dirDeployment)); } /** @@ -180,9 +234,120 @@ ApplicationInfo appInfo = null; try { + String appName = dc.getCommandParameters(DeployCommandParameters.class).name; + + // we don't need to handle rollback here, this is taken care of + // in e.g. ApplicationLifecycle... + Transaction tx = null; + if(reuseAppEntry != null) { + try { + Application app = getApplication(reuseAppEntry); + int retries = 100; + + while(app == null) { + if(--retries < 0) { + break; + } + + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + // ignore here... + } + + app = getApplication(reuseAppEntry); + } + + if(app == null) { + logger.logp(Level.WARNING, "OSGiDeploymentRequest", "deploy", + "[{0}] Failed to get the application config for ''{1}'' - falling back to new configuration", + new Object[]{String.valueOf(b.getBundleId()), reuseAppEntry}); + reuseAppEntry = null; + } else { + final DeployCommandParameters params = + dc.getCommandParameters(DeployCommandParameters.class); + // set the application registration name... + params.name = app.getName(); + + // set some properties required by the Deployment... + final Properties appProps = dc.getAppProps(); + appProps.setProperty(ServerTags.LOCATION, app.getLocation()); + appProps.setProperty(ServerTags.OBJECT_TYPE, app.getObjectType()); + appProps.setProperty(ServerTags.DIRECTORY_DEPLOYED, app.getDirectoryDeployed()); + + // override the command origin, as we do an initial deployment... + // params.origin = DeployCommandParameters.Origin.load; + // params.force = true; + + // we must ensure that the application config is writable... + tx = new Transaction(); + Application app_w = tx.enroll(app); + + // set the transient application config reference... + dc.addTransientAppMetaData(Application.APPLICATION, app_w); + } + } catch(Exception x) { + if(tx != null) { + tx.rollback(); + } + logger.logp(Level.WARNING, "OSGiDeploymentRequest", "deploy", + "[" + b.getBundleId() + + "] Failed to prepare the application config for '" + + reuseAppEntry + "' - falling back to new configuration", + x); + reuseAppEntry = null; + } + } + + + if(reuseAppEntry == null) { + try { + if(deployer.isRegistered(appName)) { + // first ensure that we can proceed with deployment... + logger.log(Level.WARNING, + "{0} - Removing stale information from domain.xml to proceed deployment", + b); + deployer.unregisterAppFromDomainXML(appName, getInstanceName(), false); + } + + logger.logp(Level.WARNING, "OSGiDeploymentRequest", "deploy", + "[{0}] Preparing application config named ''{1}''", + new Object[]{String.valueOf(b.getBundleId()), appName}); + + tx = deployer.prepareAppConfigChanges(dc); + } catch(Exception x) { + throw new RuntimeException("Failed to prepare deployment of bundle " + + "[ " + b + " ], root cause: " + x.getMessage(), x); + } + } + appInfo = deployer.deploy(dc); if (appInfo != null) { + if(reuseAppEntry == null) { + try { + logger.logp(Level.WARNING, "OSGiDeploymentRequest", "deploy", + "[{0}] Registering new application named ''{1}''", + new Object[]{String.valueOf(b.getBundleId()), appName}); + + deployer.registerAppInDomainXML(appInfo, dc, tx, false); + tx = null; + } catch (Exception x) { + throw new RuntimeException("Failed to register application for bundle " + + "[ " + b + " ], root cause: " + x.getMessage(), x); + } + } + + if(tx != null) { + try { + tx.commit(); + } catch (Exception x) { + tx.rollback(); + throw new RuntimeException("Failed to register application for bundle " + + "[ " + b + " ], root cause: " + x.getMessage(), x); + } + } + // Pass in the final classloader so that it can be used to set appropriate context // while using underlying EE components in pure OSGi context like registering EJB as services. // This won't be needed if we figure out a way of navigating to the final classloader from @@ -293,6 +458,7 @@ parameters.origin = DeployCommandParameters.Origin.deploy; parameters.force = false; parameters.target = getInstanceName(); + parameters.type = "osgi"; return parameters; } @@ -313,4 +479,10 @@ String target = se.getInstanceName(); return target; } + + private Application getApplication(String appName) { + Applications apps = Globals.get(Applications.class); + + return apps.getApplication(appName); + } } Index: module/osgi-javaee-base/src/main/java/org/glassfish/osgijavaeebase/OSGiUndeploymentRequest.java =================================================================== --- module/osgi-javaee-base/src/main/java/org/glassfish/osgijavaeebase/OSGiUndeploymentRequest.java (revision 51809) +++ module/osgi-javaee-base/src/main/java/org/glassfish/osgijavaeebase/OSGiUndeploymentRequest.java (working copy) @@ -120,6 +120,12 @@ cleanup(dc.getSourceDir()); } postUndeploy(); + + try { + deployer.unregisterAppFromDomainXML(appInfo.getName(), env.getInstanceName()); + } catch(Exception e) { + logger.log(Level.WARNING, "Module {0} not found in configuration", appInfo.getName()); + } } protected abstract OSGiDeploymentContext getDeploymentContextImpl(ActionReport reporter, Logger logger, ReadableArchive source, UndeployCommandParameters undeployParams, ServerEnvironmentImpl env, Bundle bundle) throws Exception;