geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ga...@apache.org
Subject svn commit: r1195653 - in /geronimo/server/trunk: framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/ framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/eba/ framework/mo...
Date Mon, 31 Oct 2011 20:25:38 GMT
Author: gawor
Date: Mon Oct 31 20:25:37 2011
New Revision: 1195653

URL: http://svn.apache.org/viewvc?rev=1195653&view=rev
Log:
GERONIMO-6209: Class hot swap for OSGi applications

Added:
    geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationUpdateHelper.java   (with props)
Modified:
    geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/GeronimoDeploymentManager.java
    geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/eba/EBADeploymentManager.java
    geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/jmx/ExtendedDeploymentManager.java
    geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/jmx/RemoteDeploymentManager.java
    geronimo/server/trunk/framework/modules/geronimo-management/src/main/java/org/apache/geronimo/management/geronimo/JVM.java
    geronimo/server/trunk/framework/modules/geronimo-transformer/pom.xml
    geronimo/server/trunk/framework/modules/geronimo-transformer/src/main/java/org/apache/geronimo/transformer/TransformerAgent.java
    geronimo/server/trunk/plugins/aries/geronimo-aries/pom.xml
    geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationGBean.java
    geronimo/server/trunk/plugins/j2ee/geronimo-j2ee/pom.xml
    geronimo/server/trunk/plugins/j2ee/geronimo-j2ee/src/main/java/org/apache/geronimo/j2ee/management/impl/JVMImpl.java

Modified: geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/GeronimoDeploymentManager.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/GeronimoDeploymentManager.java?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/GeronimoDeploymentManager.java (original)
+++ geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/GeronimoDeploymentManager.java Mon Oct 31 20:25:37 2011
@@ -34,4 +34,6 @@ public interface GeronimoDeploymentManag
 
     public <T> T getImplementation(Class<T> clazz);
     public URL[] getRepositories();
+    public boolean isRedefineClassesSupported();
+    
 }

Modified: geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/eba/EBADeploymentManager.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/eba/EBADeploymentManager.java?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/eba/EBADeploymentManager.java (original)
+++ geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/eba/EBADeploymentManager.java Mon Oct 31 20:25:37 2011
@@ -20,20 +20,19 @@ package org.apache.geronimo.deployment.p
 import java.io.File;
 
 import org.apache.geronimo.gbean.AbstractName;
-import org.apache.geronimo.kernel.GBeanNotFoundException;
-import org.apache.geronimo.kernel.NoSuchOperationException;
 import org.apache.geronimo.kernel.repository.Artifact;
 
-
 public interface EBADeploymentManager {
     
     public Artifact[] getEBAConfigurationIds();
     
-    public long[] getEBAContentBundleIds(AbstractName applicationGBeanName) throws GBeanNotFoundException, NoSuchOperationException, Exception;
+    public long[] getEBAContentBundleIds(AbstractName applicationGBeanName) throws Exception;
+    
+    public String getEBAContentBundleSymbolicName(AbstractName applicationGBeanName, long bundleId) throws Exception;
     
-    public String getEBAContentBundleSymbolicName(AbstractName applicationGBeanName, long bundleId) throws GBeanNotFoundException, NoSuchOperationException, Exception;
+    public void updateEBAContent(AbstractName applicationGBeanName, long bundleId, File bundleFile) throws Exception;
     
-    public void updateEBAContent(AbstractName applicationGBeanName, long bundleId, File bundleFile) throws GBeanNotFoundException, NoSuchOperationException, Exception;
+    public boolean hotSwapEBAContent(AbstractName applicationGBeanName, long bundleId, File changesFile, boolean updateArchive) throws Exception;
     
     public AbstractName getApplicationGBeanName(Artifact configurationId);
     

Modified: geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/jmx/ExtendedDeploymentManager.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/jmx/ExtendedDeploymentManager.java?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/jmx/ExtendedDeploymentManager.java (original)
+++ geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/jmx/ExtendedDeploymentManager.java Mon Oct 31 20:25:37 2011
@@ -34,7 +34,6 @@ import org.apache.geronimo.gbean.Abstrac
 import org.apache.geronimo.gbean.AbstractNameQuery;
 import org.apache.geronimo.kernel.GBeanNotFoundException;
 import org.apache.geronimo.kernel.InvalidGBeanException;
-import org.apache.geronimo.kernel.NoSuchOperationException;
 import org.apache.geronimo.kernel.config.ConfigurationInfo;
 import org.apache.geronimo.kernel.config.ConfigurationModuleType;
 import org.apache.geronimo.kernel.config.NoSuchStoreException;
@@ -284,13 +283,13 @@ public abstract class ExtendedDeployment
         return result.size() == 0 ? null : result.toArray(new Artifact[result.size()]);
     }
     
-    public long[] getEBAContentBundleIds(AbstractName applicationGBeanName) throws GBeanNotFoundException, NoSuchOperationException, Exception{
+    public long[] getEBAContentBundleIds(AbstractName applicationGBeanName) throws Exception {
         long[] ids = (long[])kernel.getAttribute(applicationGBeanName, "applicationContentBundleIds");
         
         return ids;
     }
     
-    public String getEBAContentBundleSymbolicName(AbstractName applicationGBeanName, long bundleId) throws GBeanNotFoundException, NoSuchOperationException, Exception{
+    public String getEBAContentBundleSymbolicName(AbstractName applicationGBeanName, long bundleId) throws Exception {
         Object name = kernel.invoke(applicationGBeanName, "getApplicationContentBundleSymbolicName", new Object[]{bundleId}, new String[]{long.class.getName()});
         if (name!=null) return (String)name;
         
@@ -300,8 +299,19 @@ public abstract class ExtendedDeployment
     /**
      * Only support local bundle update
      */
-    public void updateEBAContent(AbstractName applicationGBeanName, long bundleId, File bundleFile) throws GBeanNotFoundException, NoSuchOperationException, Exception{
-        kernel.invoke(applicationGBeanName, "updateApplicationContent", new Object[]{bundleId, bundleFile}, new String[]{long.class.getName(), bundleFile.getClass().getName()});
+    public void updateEBAContent(AbstractName applicationGBeanName, long bundleId, File bundleFile) throws Exception {
+        Object[] arguments = new Object[] {bundleId, bundleFile};
+        String[] argumentTypes = new String[] {long.class.getName(), File.class.getName()};
+        kernel.invoke(applicationGBeanName, "updateApplicationContent", arguments, argumentTypes);
+    }
+    
+    /**
+     * Only support local bundle update
+     */
+    public boolean hotSwapEBAContent(AbstractName applicationGBeanName, long bundleId, File changesFile, boolean updateArchive) throws Exception {
+        Object[] arguments = new Object[] {bundleId, changesFile, updateArchive};
+        String[] argumentTypes = new String[] {long.class.getName(), File.class.getName(), boolean.class.getName()};
+        return (Boolean) kernel.invoke(applicationGBeanName, "hotSwapApplicationContent", arguments, argumentTypes);
     }
     
     public AbstractName getApplicationGBeanName(Artifact configurationId) {
@@ -384,4 +394,19 @@ public abstract class ExtendedDeployment
             return null;
         }
     }
+    
+    public boolean isRedefineClassesSupported() {
+        AbstractNameQuery jvmBeanQueary = new AbstractNameQuery("org.apache.geronimo.management.JVM");
+        Set<AbstractName> beanNames = kernel.listGBeans(jvmBeanQueary);
+        if (beanNames == null || beanNames.isEmpty()) {
+            return false;
+        }
+        try {
+            Boolean value = (Boolean) kernel.getAttribute(beanNames.iterator().next(), "redefineClassesSupported");
+            return (value != null) ? value.booleanValue() : false;
+        } catch (Exception e) {
+            log.debug("Error invoking JVM MBean", e);
+            return false;
+        }
+    }
 }

Modified: geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/jmx/RemoteDeploymentManager.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/jmx/RemoteDeploymentManager.java?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/jmx/RemoteDeploymentManager.java (original)
+++ geronimo/server/trunk/framework/modules/geronimo-deploy-jsr88/src/main/java/org/apache/geronimo/deployment/plugin/jmx/RemoteDeploymentManager.java Mon Oct 31 20:25:37 2011
@@ -204,12 +204,21 @@ public class RemoteDeploymentManager ext
     }
 
     @Override
-    public void updateEBAContent(AbstractName applicationGBeanName, long bundleId, File newfile) throws GBeanNotFoundException, NoSuchOperationException, Exception{
+    public void updateEBAContent(AbstractName applicationGBeanName, long bundleId, File bundleFile) throws GBeanNotFoundException, NoSuchOperationException, Exception{
         if(!isSameMachine) {
             throw new UnsupportedOperationException("Update EBA content operation is not supportted from a remote JMX connection");
         }
         
-        super.updateEBAContent(applicationGBeanName, bundleId, newfile);
+        super.updateEBAContent(applicationGBeanName, bundleId, bundleFile);
+    }
+    
+    @Override
+    public boolean hotSwapEBAContent(AbstractName applicationGBeanName, long bundleId, File changesFile, boolean updateArchive) throws GBeanNotFoundException, NoSuchOperationException, Exception{
+        if(!isSameMachine) {
+            throw new UnsupportedOperationException("Update EBA content operation is not supportted from a remote JMX connection");
+        }
+        
+        return super.hotSwapEBAContent(applicationGBeanName, bundleId, changesFile, updateArchive);
     }
     
     @Override

Modified: geronimo/server/trunk/framework/modules/geronimo-management/src/main/java/org/apache/geronimo/management/geronimo/JVM.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/framework/modules/geronimo-management/src/main/java/org/apache/geronimo/management/geronimo/JVM.java?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/framework/modules/geronimo-management/src/main/java/org/apache/geronimo/management/geronimo/JVM.java (original)
+++ geronimo/server/trunk/framework/modules/geronimo-management/src/main/java/org/apache/geronimo/management/geronimo/JVM.java Mon Oct 31 20:25:37 2011
@@ -44,4 +44,6 @@ public interface JVM extends org.apache.
      */
     SystemLog getSystemLog();
 
+    boolean isRedefineClassesSupported();
+    
 }

Modified: geronimo/server/trunk/framework/modules/geronimo-transformer/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/framework/modules/geronimo-transformer/pom.xml?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/framework/modules/geronimo-transformer/pom.xml (original)
+++ geronimo/server/trunk/framework/modules/geronimo-transformer/pom.xml Mon Oct 31 20:25:37 2011
@@ -48,6 +48,7 @@
                 <configuration>
                     <instructions>
                         <Premain-Class>org.apache.geronimo.transformer.TransformerAgent</Premain-Class>
+                        <Can-Redefine-Classes>true</Can-Redefine-Classes>
                     </instructions>
                 </configuration>
             </plugin>

Modified: geronimo/server/trunk/framework/modules/geronimo-transformer/src/main/java/org/apache/geronimo/transformer/TransformerAgent.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/framework/modules/geronimo-transformer/src/main/java/org/apache/geronimo/transformer/TransformerAgent.java?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/framework/modules/geronimo-transformer/src/main/java/org/apache/geronimo/transformer/TransformerAgent.java (original)
+++ geronimo/server/trunk/framework/modules/geronimo-transformer/src/main/java/org/apache/geronimo/transformer/TransformerAgent.java Mon Oct 31 20:25:37 2011
@@ -16,18 +16,28 @@
  */
 package org.apache.geronimo.transformer;
 
+import java.lang.instrument.ClassDefinition;
 import java.lang.instrument.Instrumentation;
 import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.UnmodifiableClassException;
 
 /**
  * @version $Rev$ $Date$
  */
 public class TransformerAgent {
 
+    private static final String REDEFINE_CLASSES_PROPERTY = "org.apache.geronimo.transformer.redefineClasses";
+    
+    private static Instrumentation instrumentation;
     private static final TransformerCollection transformerCollection = new TransformerCollection();
 
     public static void premain(String args, Instrumentation inst) {
         inst.addTransformer(transformerCollection);
+        
+        String redefineClasses = System.getProperty(REDEFINE_CLASSES_PROPERTY, "true");        
+        if ("true".equalsIgnoreCase(redefineClasses)) {
+            instrumentation = inst;
+        }
     }
 
     public static void addTransformer(ClassFileTransformer classFileTransformer) {
@@ -37,4 +47,21 @@ public class TransformerAgent {
     public static void removeTransformer(ClassFileTransformer classFileTransformer) {
         transformerCollection.removeTransformer(classFileTransformer);
     }
+    
+    public static boolean isRedefineClassesSupported() {
+        return instrumentation != null && instrumentation.isRedefineClassesSupported();
+    }
+    
+    public static void redefine(ClassDefinition... definitions) throws UnmodifiableClassException {
+        if (!isRedefineClassesSupported()) {
+            throw new UnmodifiableClassException("Class redefinition is not supported");
+        }
+        try {
+            instrumentation.redefineClasses(definitions);
+        } catch (ClassNotFoundException e) {
+            UnmodifiableClassException ex = new UnmodifiableClassException();
+            ex.initCause(e);
+            throw ex;
+        }
+    }
 }

Modified: geronimo/server/trunk/plugins/aries/geronimo-aries/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/aries/geronimo-aries/pom.xml?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/plugins/aries/geronimo-aries/pom.xml (original)
+++ geronimo/server/trunk/plugins/aries/geronimo-aries/pom.xml Mon Oct 31 20:25:37 2011
@@ -40,6 +40,12 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.geronimo.framework</groupId>
+            <artifactId>geronimo-transformer</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.aries</groupId>
             <artifactId>org.apache.aries.util</artifactId>                
         </dependency>

Modified: geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationGBean.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationGBean.java?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationGBean.java (original)
+++ geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationGBean.java Mon Oct 31 20:25:37 2011
@@ -17,25 +17,12 @@
 package org.apache.geronimo.aries;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Enumeration;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipOutputStream;
 
 import org.apache.aries.application.ApplicationMetadataFactory;
 import org.apache.aries.application.DeploymentContent;
@@ -56,17 +43,13 @@ import org.apache.geronimo.gbean.annotat
 import org.apache.geronimo.gbean.annotation.SpecialAttributeType;
 import org.apache.geronimo.kernel.Kernel;
 import org.apache.geronimo.kernel.repository.Artifact;
-import org.apache.geronimo.kernel.util.IOUtils;
 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.FrameworkEvent;
-import org.osgi.framework.FrameworkListener;
 import org.osgi.framework.ServiceException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.Version;
-import org.osgi.service.packageadmin.ExportedPackage;
 import org.osgi.service.packageadmin.PackageAdmin;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -82,6 +65,7 @@ public class ApplicationGBean implements
     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;
@@ -94,6 +78,7 @@ public class ApplicationGBean implements
         this.bundle = bundle;
         this.installer = installer;
         this.configId = configId;
+        this.updateHelper = new ApplicationUpdateHelper(this);
                 
         BundleContext bundleContext = bundle.getBundleContext();
 
@@ -155,221 +140,46 @@ public class ApplicationGBean implements
         Bundle bundle = getBundle(bundleId);
         return (bundle != null) ? bundle.getSymbolicName() : null;
     }
-
-    public synchronized void updateApplicationContent(long bundleId, File bundleFile) throws Exception {
+    
+    /*
+     * 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 = getBundle(bundleId);
-
         if (targetBundle == null) {
             throw new IllegalArgumentException("Could not find bundle with id " + bundleId + " in the application");
         }
-
-        String applicationName = application.getApplicationMetadata().getApplicationScope();
-        String bundleName = targetBundle.getSymbolicName();
         
-        LOG.info("Updating {} bundle in {} application", bundleName, applicationName);
-                
-        BundleContext context = bundle.getBundleContext();
-
-        ServiceReference reference = null;
-        RefreshListener refreshListener = null;
-        try {
-            // stop the bundle
-            targetBundle.stop();
-
-            // update the bundle
-            FileInputStream fi = null;
-            try {
-                fi = new FileInputStream(bundleFile);
-                targetBundle.update(fi);
-            } finally {
-                IOUtils.close(fi);
-            }
-
-            reference = context.getServiceReference(PackageAdmin.class.getName());
-            PackageAdmin packageAdmin = (PackageAdmin) context.getService(reference);
-            
-            Bundle[] bundles = new Bundle [] { targetBundle };
-            // resolve the bundle
-            if (!packageAdmin.resolveBundles(bundles)) {
-                StringBuilder builder = new StringBuilder();
-                builder.append("Updated ").append(bundleName).append(" bundle cannot be resolved.");
-                
-                // check for resolver errors
-                ResolverErrorAnalyzer errorAnalyzer = new ResolverErrorAnalyzer(context);
-                String resolverErrors = errorAnalyzer.getErrorsAsString(Arrays.asList(bundles));
-                if (resolverErrors != null) {
-                    builder.append(" ").append(resolverErrors);
-                }
-                
-                throw new BundleException(builder.toString());
-            }
-            
-            Set<Bundle> dependents = new HashSet<Bundle>();
-            collectDependentBundles(packageAdmin, dependents, targetBundle);
-            if (!dependents.isEmpty()) {
-                String bundleListString = bundleCollectionToString(dependents);
-                LOG.info("Update of {} bundle will cause the following bundles to be refreshed: {}", bundleName, bundleListString);
-            }
-            
-            // install listener for package refresh
-            refreshListener = new RefreshListener();
-            context.addFrameworkListener(refreshListener);
-
-            // refresh the bundle - this happens asynchronously
-            packageAdmin.refreshPackages(bundles);
-
-            // update application archive
-            try {
-                updateArchive(targetBundle, bundleFile);
-            } 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.getMessage());
-            }
-
-            // wait for package refresh to finish
-            refreshListener.waitForRefresh(10 * 1000);
-
-            // start the bundle
-            if (BundleUtils.canStart(targetBundle)) {
-                targetBundle.start(Bundle.START_TRANSIENT);
-            }
-            
-            
-            LOG.info("Bundle {} was successfully updated in {} application", bundleName, applicationName);
-            
-        } catch (Exception e) {
-            LOG.error("Error updating " + bundleName + " bundle in " + applicationName + " application", e);
-            throw new Exception("Error updating application: " + e.getMessage());
-        } finally {
-            if (refreshListener != null) {
-                context.removeFrameworkListener(refreshListener);
-            }
-            if (reference != null) {
-                context.ungetService(reference);
-            }
-        }
+        updateHelper.updateBundle(targetBundle, file);
     }
     
-    private class RefreshListener implements FrameworkListener {
-
-        public CountDownLatch latch = new CountDownLatch(1);
-
-        public void frameworkEvent(FrameworkEvent event) {
-            if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
-                latch.countDown();
-            }
-        }
-
-        public void waitForRefresh(int timeout) {
-            try {
-                latch.await(timeout, TimeUnit.MILLISECONDS);
-            } catch (InterruptedException e) {
-                // ignore
-            }
+    /*
+     * 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 = getBundle(bundleId);
+        if (targetBundle == null) {
+            throw new IllegalArgumentException("Could not find bundle with id " + bundleId + " in the application");
         }
-    }
 
-    private void collectDependentBundles(PackageAdmin packageAdmin, Set<Bundle> dependents, Bundle bundle) {
-        ExportedPackage[] exportedPackages = packageAdmin.getExportedPackages(bundle);
-        if (exportedPackages != null) {
-            for (ExportedPackage exportedPackage : exportedPackages) {
-                Bundle[] importingBundles = exportedPackage.getImportingBundles();
-                if (importingBundles != null) {
-                    for (Bundle importingBundle : importingBundles) {
-                        if (!dependents.contains(importingBundle)) {
-                            dependents.add(importingBundle);
-                            collectDependentBundles(packageAdmin, dependents, importingBundle);
-                        }
-                    }
-                }
-            }
-        }
+        return updateHelper.updateBundleClasses(targetBundle, changesFile, updateArchive);
     }
-    
-    private static String bundleCollectionToString(Collection<Bundle> bundles) {
-        StringBuilder builder = new StringBuilder();
-        Iterator<Bundle> iterator = bundles.iterator();
-        while(iterator.hasNext()) {
-            Bundle bundle = iterator.next();
-            builder.append(bundle.getSymbolicName());
-            builder.append(" [").append(bundle.getBundleId()).append("]");
-            if (iterator.hasNext()) {
-                builder.append(", ");
-            }
-        }
-       return builder.toString();
-    }
-    
-    private void updateArchive(Bundle bundle, File bundleFile) throws IOException {
+   
+    protected File getApplicationArchive() throws IOException {
         File ebaArchive = installer.getApplicationLocation(configId);
         if (ebaArchive == null || !ebaArchive.exists()) {
             throw new IOException("Cannot locate application archive for " + configId);
         }
-
-        File newEbaArchive = new File(ebaArchive.getAbsoluteFile() + ".new");
-
-        URI bundleLocation = URI.create(bundle.getLocation());
-        String bundleNameInApp = bundleLocation.getPath();
-        if (bundleNameInApp.startsWith("/")) {
-            bundleNameInApp = bundleNameInApp.substring(1);
-        }
-
-        LOG.debug("Updating {} application archive with new contents for {}", ebaArchive, bundleNameInApp);
-
-        ZipFile oldZipFile = null;
-        ZipOutputStream newZipFile = null;
-        try {
-            newZipFile = new ZipOutputStream(new FileOutputStream(newEbaArchive));
-            oldZipFile = new ZipFile(ebaArchive);
-            Enumeration<? extends ZipEntry> entries = oldZipFile.entries();
-            byte[] buffer = new byte[4096];
-            while (entries.hasMoreElements()) {
-                ZipEntry entry = (ZipEntry) entries.nextElement();
-
-                InputStream in = null;
-                if (entry.getName().equals(bundleNameInApp)) {
-                    in = new FileInputStream(bundleFile);
-                    LOG.debug("Updating contents of {} with {}", bundleNameInApp, bundleFile.getAbsolutePath());
-                } else {
-                    in = oldZipFile.getInputStream(entry);
-                }
-                try {
-                    newZipFile.putNextEntry(new ZipEntry(entry.getName()));
-                    try {
-                        int count;
-                        while ((count = in.read(buffer)) > 0) {
-                            newZipFile.write(buffer, 0, count);
-                        }
-                    } finally {
-                        newZipFile.closeEntry();
-                    }
-                } finally {
-                    IOUtils.close(in);
-                }
-            }
-        } catch (IOException e) {
-            LOG.debug("Error updating application archive", e);
-        } finally {
-            if (oldZipFile != null) {
-                try {
-                    oldZipFile.close();
-                } catch (IOException ignore) {
-                }
-            }
-            IOUtils.close(newZipFile);
-        }
-
-        if (ebaArchive.delete()) {
-            if (!newEbaArchive.renameTo(ebaArchive)) {
-                throw new IOException("Error renaming application archive");
-            } else {
-                LOG.debug("Application archive was successfully updated.");
-            }
-        } else {
-            throw new IOException("Error deleting existing application archive");
-        }
+        return ebaArchive;
     }
-
+    
     protected Bundle getBundle() {
         return bundle;
     }
@@ -386,16 +196,20 @@ public class ApplicationGBean implements
         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.", application.getApplicationMetadata().getApplicationScope());
+            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.", application.getApplicationMetadata().getApplicationScope());
+            LOG.debug("Application {} is resolved.", getApplicationName());
         }
         return meta;
     }
@@ -520,13 +334,13 @@ public class ApplicationGBean implements
     }
     
     public void doStart() throws Exception {
-        LOG.debug("Starting {} application.", application.getApplicationMetadata().getApplicationScope());
+        LOG.debug("Starting {} application.", getApplicationName());
         
         applicationState = ApplicationState.STARTING;
         try {
             startApplicationBundles();
             applicationState = ApplicationState.ACTIVE;
-            LOG.debug("Application {} started successfully.", application.getApplicationMetadata().getApplicationScope());
+            LOG.debug("Application {} started successfully.", getApplicationName());
         } catch (BundleException be) {
             applicationState = ApplicationState.INSTALLED;
             
@@ -541,9 +355,7 @@ public class ApplicationGBean implements
                 rootMessage = resolverErrors;
             }
 
-            String message = MessageFormat.format("Error starting {0} application. {1}", 
-                                                  application.getApplicationMetadata().getApplicationScope(), 
-                                                  rootMessage);
+            String message = MessageFormat.format("Error starting {0} application. {1}", getApplicationName(), rootMessage);
             
             if (getFailOnStartError()) {
                 throw new BundleException(message, rootException);
@@ -581,7 +393,7 @@ public class ApplicationGBean implements
     }
     
     public void doStop() {
-        LOG.debug("Stopping {} application.", application.getApplicationMetadata().getApplicationScope());
+        LOG.debug("Stopping {} application.", getApplicationName());
         
         for (Bundle bundle : applicationBundles) {
             try {
@@ -617,7 +429,7 @@ public class ApplicationGBean implements
     }
     
     protected void applicationUninstall() {
-        LOG.debug("Uninstalling {} application.", application.getApplicationMetadata().getApplicationScope());
+        LOG.debug("Uninstalling {} application.", getApplicationName());
 
         try {
             installer.getConfigurationManager().unloadConfiguration(configId);

Added: geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationUpdateHelper.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationUpdateHelper.java?rev=1195653&view=auto
==============================================================================
--- geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationUpdateHelper.java (added)
+++ geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationUpdateHelper.java Mon Oct 31 20:25:37 2011
@@ -0,0 +1,524 @@
+/**
+ *  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.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.instrument.ClassDefinition;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.geronimo.kernel.util.IOUtils;
+import org.apache.geronimo.transformer.TransformerAgent;
+import org.apache.xbean.osgi.bundle.util.BundleUtils;
+import org.apache.xbean.osgi.bundle.util.HeaderParser;
+import org.apache.xbean.osgi.bundle.util.HeaderParser.HeaderElement;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @version $Rev:385232 $ $Date$
+ */
+public class ApplicationUpdateHelper {
+        
+    private static final Logger LOG = LoggerFactory.getLogger(ApplicationUpdateHelper.class);
+    
+    private final ApplicationGBean applicationGBean;
+    
+    public ApplicationUpdateHelper(ApplicationGBean applicationGBean) {
+        this.applicationGBean = applicationGBean;
+    }
+    
+    public void updateBundle(Bundle targetBundle, File bundleFile) throws Exception {
+        
+        String applicationName = applicationGBean.getApplicationName();
+        String bundleName = targetBundle.getSymbolicName();
+        
+        LOG.info("Updating {} bundle in {} application", bundleName, applicationName);
+                
+        BundleContext context = applicationGBean.getBundle().getBundleContext();
+
+        ServiceReference reference = null;
+        RefreshListener refreshListener = null;
+        try {
+            // stop the bundle
+            targetBundle.stop();
+
+            // update the bundle
+            FileInputStream fi = null;
+            try {
+                fi = new FileInputStream(bundleFile);
+                targetBundle.update(fi);
+            } finally {
+                close(fi);
+            }
+
+            reference = context.getServiceReference(PackageAdmin.class.getName());
+            PackageAdmin packageAdmin = (PackageAdmin) context.getService(reference);
+            
+            Bundle[] bundles = new Bundle [] { targetBundle };
+            // resolve the bundle
+            if (!packageAdmin.resolveBundles(bundles)) {
+                StringBuilder builder = new StringBuilder();
+                builder.append("Updated ").append(bundleName).append(" bundle cannot be resolved.");
+                
+                // check for resolver errors
+                ResolverErrorAnalyzer errorAnalyzer = new ResolverErrorAnalyzer(context);
+                String resolverErrors = errorAnalyzer.getErrorsAsString(Arrays.asList(bundles));
+                if (resolverErrors != null) {
+                    builder.append(" ").append(resolverErrors);
+                }
+                
+                throw new BundleException(builder.toString());
+            }
+            
+            Set<Bundle> dependents = new HashSet<Bundle>();
+            collectDependentBundles(packageAdmin, dependents, targetBundle);
+            if (!dependents.isEmpty()) {
+                String bundleListString = bundleCollectionToString(dependents);
+                LOG.info("Update of {} bundle will cause the following bundles to be refreshed: {}", bundleName, bundleListString);
+            }
+            
+            // install listener for package refresh
+            refreshListener = new RefreshListener();
+            context.addFrameworkListener(refreshListener);
+
+            // refresh the bundle - this happens asynchronously
+            packageAdmin.refreshPackages(bundles);
+
+            // update application archive
+            try {
+                updateArchive(targetBundle, bundleFile);
+            } 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.getMessage());
+            }
+
+            // wait for package refresh to finish
+            refreshListener.waitForRefresh(10 * 1000);
+
+            // start the bundle
+            if (BundleUtils.canStart(targetBundle)) {
+                targetBundle.start(Bundle.START_TRANSIENT);
+            }
+            
+            LOG.info("Bundle {} was successfully updated in {} application", bundleName, applicationName);
+            
+        } catch (Exception e) {
+            LOG.error("Error updating " + bundleName + " bundle in " + applicationName + " application", e);
+            throw new Exception("Error updating application: " + e.getMessage());
+        } finally {
+            if (refreshListener != null) {
+                context.removeFrameworkListener(refreshListener);
+            }
+            if (reference != null) {
+                context.ungetService(reference);
+            }
+        }
+    }
+    
+    public boolean updateBundleClasses(Bundle targetBundle, File changesFile, boolean updateArchive) throws Exception {
+        if (!TransformerAgent.isRedefineClassesSupported()) {
+            return false;
+        }
+                
+        if (!hotSwapClasses(targetBundle, changesFile)) {
+            return false;
+        }
+        
+        if (updateArchive) {
+            // create updated bundle jar & update archive
+            File updatedBundleFile = null;
+            try {
+                updatedBundleFile = createUpdatedJarFile(targetBundle, changesFile);
+                updateArchive(targetBundle, updatedBundleFile);
+            } 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.getMessage());
+            } finally {
+                deleteFile(updatedBundleFile);
+            }
+        }
+        
+        return true;
+    }
+    
+    private boolean hotSwapClasses(Bundle bundle, File changesFile) {
+        String bundleName = bundle.getSymbolicName();
+        
+        LOG.info("Checking if class hot swap can be performed for {} bundle", bundleName);
+        
+        String[] classPath = getBundleClassPath(bundle);
+        List<ClassDefinition> classDefinitionList = new ArrayList<ClassDefinition>();
+        ZipFile zipFile = null;
+        try {
+            zipFile = new ZipFile(changesFile);
+            Enumeration<? extends ZipEntry> entries = zipFile.entries();
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = entries.nextElement();
+                if (entry.isDirectory()) {
+                    continue;
+                }
+                String name = entry.getName();
+                if (!name.endsWith(".class")) {
+                    throw new IllegalStateException("Non class file found: " + name);
+                }
+                
+                // compute & load existing class
+                String className = getClassName(classPath, name);
+                Class<?> clazz = bundle.loadClass(className);
+                
+                // get the new class bytes
+                InputStream in = zipFile.getInputStream(entry);
+                byte[] clazzBytes = IOUtils.getBytes(in);
+                
+                classDefinitionList.add(new ClassDefinition(clazz, clazzBytes));
+            }
+        } catch (Exception e) {
+            LOG.info("Hot swap cannot be performed for {} bundle: {}", bundleName, e.getMessage());
+            LOG.debug("Hot swap cannot be performed for " + bundleName + " bundle", e);
+            return false;
+        } finally {
+            close(zipFile);
+        }
+        
+        try {
+            LOG.info("Attempting to hot swap the following classes for {} bundle: {}", bundleName, classDefinitionToString(classDefinitionList));
+            ClassDefinition[] classDefinitions = new ClassDefinition[classDefinitionList.size()];
+            classDefinitionList.toArray(classDefinitions);
+            TransformerAgent.redefine(classDefinitions);
+            LOG.info("Hot swap was successfully performed for {} bundle", bundleName);
+            return true;
+        } catch (Exception e) {
+            LOG.info("Unable to perform class hot swap for {} bundle: {}", bundleName, e.getMessage());
+            LOG.debug("Error performing hot swap for " + bundleName + " bundle", e);
+            return false;
+        }
+    }
+    
+    private String[] getBundleClassPath(Bundle bundle) {
+        String classPathHeader = bundle.getHeaders().get(Constants.BUNDLE_CLASSPATH);
+        if (classPathHeader == null) {
+            return null;
+        }
+        List<HeaderElement> classPathElements = HeaderParser.parseHeader(classPathHeader);
+        String[] classPath = new String[classPathElements.size()];
+        for (int i = 0; i < classPathElements.size(); i++) {
+            String classPathName = classPathElements.get(i).getName();
+            if (classPathName.equals(".") || classPathName.equals("/")) {
+                classPathName = "";
+            } else if (classPathName.endsWith("/")) {
+                classPathName = classPathName.substring(0, classPathName.length() - 1);
+            }
+            /*
+             * Could check & ignore jar class path entries but treat them as directory
+             * entries for now. 
+             */
+            classPath[i] = classPathName;
+        }
+        return classPath;
+    }
+    
+    /*
+     * Get the class name to load based on the Bundle-ClassPath entries.
+     */
+    private String getClassName(String[] classPath, String classEntryName) {
+        if (classPath != null) {
+            /*
+             * It is possible that the class entry name could match multiple class path entries
+             * but that is pretty rare - so let's ignore this case for now.
+             */
+            for (String classPathEntry : classPath) {            
+                if (classEntryName.startsWith(classPathEntry)) {
+                    classEntryName = classEntryName.substring(classPathEntry.length() + 1);
+                    break;
+                }
+            }
+        }
+        return classEntryName.substring(0, classEntryName.length() - ".class".length()).replace('/', '.');
+    }
+    
+    private class RefreshListener implements FrameworkListener {
+
+        public CountDownLatch latch = new CountDownLatch(1);
+
+        public void frameworkEvent(FrameworkEvent event) {
+            if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
+                latch.countDown();
+            }
+        }
+
+        public void waitForRefresh(int timeout) {
+            try {
+                latch.await(timeout, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+    }
+
+    private void collectDependentBundles(PackageAdmin packageAdmin, Set<Bundle> dependents, Bundle bundle) {
+        ExportedPackage[] exportedPackages = packageAdmin.getExportedPackages(bundle);
+        if (exportedPackages != null) {
+            for (ExportedPackage exportedPackage : exportedPackages) {
+                Bundle[] importingBundles = exportedPackage.getImportingBundles();
+                if (importingBundles != null) {
+                    for (Bundle importingBundle : importingBundles) {
+                        if (!dependents.contains(importingBundle)) {
+                            dependents.add(importingBundle);
+                            collectDependentBundles(packageAdmin, dependents, importingBundle);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    private static String bundleCollectionToString(Collection<Bundle> bundles) {
+        StringBuilder builder = new StringBuilder();
+        Iterator<Bundle> iterator = bundles.iterator();
+        while(iterator.hasNext()) {
+            Bundle bundle = iterator.next();
+            builder.append(bundle.getSymbolicName());
+            builder.append(" [").append(bundle.getBundleId()).append("]");
+            if (iterator.hasNext()) {
+                builder.append(", ");
+            }
+        }
+       return builder.toString();
+    }
+    
+    private static String classDefinitionToString(Collection<ClassDefinition> definitions) {
+        StringBuilder builder = new StringBuilder();
+        Iterator<ClassDefinition> iterator = definitions.iterator();
+        while(iterator.hasNext()) {
+            ClassDefinition definition = iterator.next();
+            builder.append(definition.getDefinitionClass().getName());
+            if (iterator.hasNext()) {
+                builder.append(", ");
+            }
+        }
+       return builder.toString();
+    }
+    
+    private File createUpdatedJarFile(Bundle targetBundle, File changesFile) throws IOException {
+        File ebaArchive = applicationGBean.getApplicationArchive();
+        
+        String bundleNameInApp = getBundleNameInArchive(targetBundle);
+        
+        ZipFile zipFile = null;
+        File updatedFile = null;
+        try {
+            zipFile = new ZipFile(ebaArchive);
+            ZipEntry entry = zipFile.getEntry(bundleNameInApp);
+            if (entry == null) {
+                throw new IOException("Unable to locate " + bundleNameInApp + " in " + ebaArchive);
+            }           
+            updatedFile = File.createTempFile(bundleNameInApp, ".jar");
+            JarInputStream jarIn = new JarInputStream(zipFile.getInputStream(entry));
+            updateJarFile(jarIn, changesFile, updatedFile);            
+        } catch (IOException e) {
+            deleteFile(updatedFile);
+            throw e;
+        } finally {
+            close(zipFile);
+        }
+        
+        return updatedFile;
+    }
+    
+    private static void updateJarFile(JarInputStream jarIn, File changesFile, File outputFile) throws IOException {
+        Map<String, ZipEntry> updatedEntries = new HashMap<String, ZipEntry>();
+        ZipFile zipFile = null;
+        JarOutputStream jarOut = null;
+        try {
+            // build map of updated entries
+            zipFile = new ZipFile(changesFile);
+            Enumeration<? extends ZipEntry> entries = zipFile.entries();
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = entries.nextElement();
+                if (entry.isDirectory()) {
+                    continue;
+                }
+                updatedEntries.put(entry.getName(), entry);
+            }
+            
+            // get manifest
+            Manifest manifest = jarIn.getManifest();
+            ZipEntry manifestEntry = updatedEntries.remove(JarFile.MANIFEST_NAME);
+            if (manifestEntry != null) {
+                manifest = new Manifest(zipFile.getInputStream(manifestEntry));
+            }
+            
+            // update the contents of the jar file 
+            jarOut = new JarOutputStream(new FileOutputStream(outputFile), manifest);
+            JarEntry entry = null;
+            byte[] buffer = new byte[4096];
+            while ( (entry = jarIn.getNextJarEntry()) != null) {
+                ZipEntry source = updatedEntries.remove(entry.getName());
+                if (source == null) {
+                    copyZipEntry(jarOut, entry, jarIn, buffer);
+                } else {
+                    InputStream in = zipFile.getInputStream(source);
+                    try {
+                        copyZipEntry(jarOut, source, in, buffer);
+                    } finally {
+                        close(in);
+                    }
+                }
+            }
+
+            // if updatedEntires is not empty now - we have a problem
+            if (!updatedEntries.isEmpty()) {
+                throw new IOException("Some jar entries were not updated: " + updatedEntries.keySet());
+            }
+        } finally {
+            close(jarIn);
+            close(zipFile);
+            close(jarOut);
+        }
+    }
+    
+    private void updateArchive(Bundle bundle, File bundleFile) throws IOException {
+        File ebaArchive = applicationGBean.getApplicationArchive();
+
+        File newEbaArchive = new File(ebaArchive.getAbsoluteFile() + ".new");
+
+        String bundleNameInApp = getBundleNameInArchive(bundle);
+
+        LOG.debug("Updating {} application archive with new contents for {}", ebaArchive, bundleNameInApp);
+
+        ZipFile oldZipFile = null;
+        ZipOutputStream newZipFile = null;
+        try {
+            newZipFile = new ZipOutputStream(new FileOutputStream(newEbaArchive));
+            oldZipFile = new ZipFile(ebaArchive);
+            Enumeration<? extends ZipEntry> entries = oldZipFile.entries();
+            byte[] buffer = new byte[4096];
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = (ZipEntry) entries.nextElement();
+                InputStream in = null;
+                if (entry.getName().equals(bundleNameInApp)) {
+                    in = new FileInputStream(bundleFile);
+                    LOG.debug("Updating contents of {} with {}", bundleNameInApp, bundleFile.getAbsolutePath());
+                } else {
+                    in = oldZipFile.getInputStream(entry);
+                }
+                try {
+                    copyZipEntry(newZipFile, new ZipEntry(entry.getName()), in, buffer);
+                } finally {
+                    close(in);
+                }
+            }
+        } catch (IOException e) {
+            LOG.debug("Error updating application archive", e);
+            newEbaArchive.delete();
+            throw e;
+        } finally {
+            close(oldZipFile);
+            close(newZipFile);
+        }
+
+        if (ebaArchive.delete()) {
+            if (!newEbaArchive.renameTo(ebaArchive)) {
+                throw new IOException("Error renaming application archive");
+            } else {
+                LOG.debug("Application archive was successfully updated.");
+            }
+        } else {
+            throw new IOException("Error deleting existing application archive");
+        }
+    }
+
+    private static void copyZipEntry(ZipOutputStream zipFile, ZipEntry entry, InputStream in, byte[] buffer) throws IOException {
+        zipFile.putNextEntry(entry);
+        try {
+            int count;
+            while ((count = in.read(buffer)) > 0) {
+                zipFile.write(buffer, 0, count);
+            }
+        } finally {
+            zipFile.closeEntry();
+        }
+    }
+    
+    private static String getBundleNameInArchive(Bundle bundle) {
+        URI bundleLocation = URI.create(bundle.getLocation());
+        String bundleNameInApp = bundleLocation.getPath();
+        if (bundleNameInApp.startsWith("/")) {
+            bundleNameInApp = bundleNameInApp.substring(1);
+        }
+        return bundleNameInApp;
+    }
+    
+
+    private static void close(ZipFile thing) {
+        if (thing != null) {
+            try {
+                thing.close();
+            } catch (Exception ignored) {
+            }
+        }
+    }
+    
+    private static void close(Closeable thing) {
+        if (thing != null) {
+            try {
+                thing.close();
+            } catch (Exception ignored) {
+            }
+        }
+    }
+    
+    private static void deleteFile(File file) {
+        if (file != null) {
+            file.delete();
+        }
+    }
+}

Propchange: geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationUpdateHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationUpdateHelper.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/server/trunk/plugins/aries/geronimo-aries/src/main/java/org/apache/geronimo/aries/ApplicationUpdateHelper.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: geronimo/server/trunk/plugins/j2ee/geronimo-j2ee/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/j2ee/geronimo-j2ee/pom.xml?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/plugins/j2ee/geronimo-j2ee/pom.xml (original)
+++ geronimo/server/trunk/plugins/j2ee/geronimo-j2ee/pom.xml Mon Oct 31 20:25:37 2011
@@ -53,6 +53,13 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.geronimo.framework</groupId>
+            <artifactId>geronimo-transformer</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.servicemix.bundles</groupId>
             <artifactId>org.apache.servicemix.bundles.howl</artifactId>
         </dependency>

Modified: geronimo/server/trunk/plugins/j2ee/geronimo-j2ee/src/main/java/org/apache/geronimo/j2ee/management/impl/JVMImpl.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/j2ee/geronimo-j2ee/src/main/java/org/apache/geronimo/j2ee/management/impl/JVMImpl.java?rev=1195653&r1=1195652&r2=1195653&view=diff
==============================================================================
--- geronimo/server/trunk/plugins/j2ee/geronimo-j2ee/src/main/java/org/apache/geronimo/j2ee/management/impl/JVMImpl.java (original)
+++ geronimo/server/trunk/plugins/j2ee/geronimo-j2ee/src/main/java/org/apache/geronimo/j2ee/management/impl/JVMImpl.java Mon Oct 31 20:25:37 2011
@@ -40,6 +40,7 @@ import org.apache.geronimo.management.ge
 import org.apache.geronimo.management.stats.BoundedRangeStatisticImpl;
 import org.apache.geronimo.management.stats.CountStatisticImpl;
 import org.apache.geronimo.management.stats.JVMStatsImpl;
+import org.apache.geronimo.transformer.TransformerAgent;
 import org.apache.geronimo.logging.SystemLog;
 
 /**
@@ -202,7 +203,11 @@ public class JVMImpl implements JVM, Sta
     public SystemLog getSystemLog() {
         return systemLog;
     }
-
+    
+    public boolean isRedefineClassesSupported() {
+        return TransformerAgent.isRedefineClassesSupported();
+    }
+    
     public static final GBeanInfo GBEAN_INFO;
 
     static {



Mime
View raw message