FELIX-1644 Reintroduce update button to update a bundle from its
location or from OBR
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@818995 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BaseUpdateInstallHelper.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BaseUpdateInstallHelper.java
new file mode 100644
index 0000000..ebf7f6d
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BaseUpdateInstallHelper.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 1997-2009 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.felix.webconsole.internal.core;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.felix.webconsole.internal.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.service.log.LogService;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+
+abstract class BaseUpdateInstallHelper extends Thread
+{
+
+ private final File bundleFile;
+
+ private final boolean refreshPackages;
+
+
+ BaseUpdateInstallHelper( String name, File bundleFile, boolean refreshPackages )
+ {
+ super( name );
+ setDaemon( true );
+
+ this.bundleFile = bundleFile;
+ this.refreshPackages = refreshPackages;
+ }
+
+
+ protected File getBundleFile()
+ {
+ return bundleFile;
+ }
+
+
+ protected abstract Bundle doRun( InputStream bundleStream ) throws BundleException;
+
+
+ protected abstract Logger getLog();
+
+
+ protected abstract Object getService( String serviceName );
+
+
+ /**
+ * @return the installed bundle or <code>null</code> if no bundle was touched
+ * @throws BundleException
+ * @throws IOException
+ */
+ protected Bundle doRun() throws BundleException, IOException
+ {
+ // now deploy the resolved bundles
+ InputStream bundleStream = null;
+ try
+ {
+ bundleStream = new FileInputStream( bundleFile );
+ return doRun( bundleStream );
+ }
+ finally
+ {
+ if ( bundleStream != null )
+ {
+ try
+ {
+ bundleStream.close();
+ }
+ catch ( IOException ignore )
+ {
+ }
+ }
+ }
+
+ }
+
+
+ public void run()
+ {
+ // wait some time for the request to settle
+ sleepSilently( 500L );
+
+ // now deploy the resolved bundles
+ try
+ {
+ // we need the package admin before we call the bundle
+ // installation or update, since we might be updating
+ // our selves in which case the bundle context will be
+ // invalid by the time we want to call the update
+ PackageAdmin pa = ( refreshPackages ) ? ( PackageAdmin ) getService( PackageAdmin.class.getName() ) : null;
+
+ Bundle bundle = doRun();
+
+ if ( pa != null && bundle != null )
+ {
+ // wait for asynchronous bundle start tasks to finish
+ sleepSilently( 2000L );
+
+ pa.refreshPackages( new Bundle[]
+ { bundle } );
+ }
+ }
+ catch ( IOException ioe )
+ {
+ getLog().log( LogService.LOG_ERROR, "Cannot install or update bundle from " + bundleFile, ioe );
+ }
+ catch ( BundleException be )
+ {
+ getLog().log( LogService.LOG_ERROR, "Cannot install or update bundle from " + bundleFile, be );
+ }
+ finally
+ {
+ if ( bundleFile != null )
+ {
+ bundleFile.delete();
+ }
+ }
+ }
+
+
+ protected void sleepSilently( long msecs )
+ {
+ try
+ {
+ sleep( msecs );
+ }
+ catch ( InterruptedException ie )
+ {
+ // don't care
+ }
+ }
+}
\ No newline at end of file
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
index 92a4b38..81075d5 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
@@ -46,6 +46,7 @@
import org.apache.felix.webconsole.ConfigurationPrinter;
import org.apache.felix.webconsole.WebConsoleConstants;
import org.apache.felix.webconsole.internal.BaseWebConsolePlugin;
+import org.apache.felix.webconsole.internal.Logger;
import org.apache.felix.webconsole.internal.Util;
import org.json.JSONArray;
import org.json.JSONException;
@@ -285,6 +286,12 @@
refresh( bundle );
success = true;
}
+ else if ( "update".equals( action ) )
+ {
+ // update the bundle
+ update( bundle );
+ success = true;
+ }
else if ( "uninstall".equals( action ) )
{
// uninstall bundle
@@ -573,6 +580,7 @@
action( jw, hasStop( bundle ), "stop", "Stop", "stop" );
}
action( jw, true, "refresh", "Refresh Package Imports", "refresh" );
+ action( jw, true, "update", "Update", "update" );
action( jw, hasUninstall( bundle ), "uninstall", "Uninstall", "delete" );
}
jw.endArray();
@@ -1179,6 +1187,26 @@
{ bundle } );
}
+
+ private void update( final Bundle bundle )
+ {
+ Thread t = new UpdateHelper( bundle, false )
+ {
+ protected Logger getLog()
+ {
+ return BundlesServlet.this.getLog();
+ }
+
+
+ protected Object getService( String serviceName )
+ {
+ return BundlesServlet.this.getService( serviceName );
+ }
+ };
+
+ t.start();
+ }
+
private final class RequestInfo
{
public final String extension;
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java
index a5cc8ce..b3b4847 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java
@@ -29,6 +29,7 @@
import org.apache.felix.webconsole.AbstractWebConsolePlugin;
import org.apache.felix.webconsole.Action;
import org.apache.felix.webconsole.internal.BaseManagementPlugin;
+import org.apache.felix.webconsole.internal.Logger;
import org.osgi.framework.*;
import org.osgi.service.log.LogService;
import org.osgi.service.packageadmin.PackageAdmin;
@@ -273,28 +274,26 @@
final boolean doStart, final boolean refreshPackages )
{
- Thread t = new InstallHelper( this, "Background Install " + bundleFile, bundleFile, refreshPackages )
+ Thread t = new InstallHelper( getBundleContext(), bundleFile, location, startlevel, doStart, refreshPackages )
{
-
- protected Bundle doRun( InputStream bundleStream ) throws BundleException
+ protected Logger getLog()
{
- Bundle bundle = getBundleContext().installBundle( location, bundleStream );
+ return InstallAction.this.getLog();
+ }
- if ( startlevel > 0 )
+
+ protected Object getService( String serviceName )
+ {
+ if ( serviceName.equals( PackageAdmin.class.getName() ) )
{
- StartLevel sl = getStartLevel();
- if ( sl != null )
- {
- sl.setBundleStartLevel( bundle, startlevel );
- }
+ return InstallAction.this.getPackageAdmin();
+ }
+ else if ( serviceName.equals( StartLevel.class.getName() ) )
+ {
+ return InstallAction.this.getStartLevel();
}
- if ( doStart )
- {
- bundle.start();
- }
-
- return bundle;
+ return null;
}
};
@@ -304,108 +303,25 @@
private void updateBackground( final Bundle bundle, final File bundleFile, final boolean refreshPackages )
{
- Thread t = new InstallHelper( this, "Background Update " + bundle.getSymbolicName() + " ("
- + bundle.getBundleId() + ")", bundleFile, refreshPackages )
+ Thread t = new UpdateHelper( bundle, bundleFile, refreshPackages )
{
-
- protected Bundle doRun( InputStream bundleStream ) throws BundleException
+ protected Logger getLog()
{
- bundle.update( bundleStream );
- return bundle;
+ return InstallAction.this.getLog();
+ }
+
+
+ protected Object getService( String serviceName )
+ {
+ if ( serviceName.equals( PackageAdmin.class.getName() ) )
+ {
+ return InstallAction.this.getPackageAdmin();
+ }
+
+ return null;
}
};
t.start();
}
-
- private static abstract class InstallHelper extends Thread
- {
-
- private final InstallAction installAction;
-
- private final File bundleFile;
-
- private final boolean refreshPackages;
-
-
- InstallHelper( InstallAction installAction, String name, File bundleFile, boolean refreshPackages )
- {
- super( name );
- setDaemon( true );
-
- this.installAction = installAction;
- this.bundleFile = bundleFile;
- this.refreshPackages = refreshPackages;
- }
-
-
- protected abstract Bundle doRun( InputStream bundleStream ) throws BundleException;
-
-
- public void run()
- {
- // wait some time for the request to settle
- sleepSilently( 500L );
-
- // now deploy the resolved bundles
- InputStream bundleStream = null;
- try
- {
- // we need the package admin before we call the bundle
- // installation or update, since we might be updating
- // our selves in which case the bundle context will be
- // invalid by the time we want to call the update
- PackageAdmin pa = ( refreshPackages ) ? installAction.getPackageAdmin() : null;
-
- bundleStream = new FileInputStream( bundleFile );
- Bundle bundle = doRun( bundleStream );
-
- if ( pa != null )
- {
- // wait for asynchronous bundle start tasks to finish
- sleepSilently( 2000L );
-
- pa.refreshPackages( new Bundle[]
- { bundle } );
- }
- }
- catch ( IOException ioe )
- {
- installAction.getLog().log( LogService.LOG_ERROR, "Cannot install or update bundle from " + bundleFile,
- ioe );
- }
- catch ( BundleException be )
- {
- installAction.getLog().log( LogService.LOG_ERROR, "Cannot install or update bundle from " + bundleFile,
- be );
- }
- finally
- {
- if ( bundleStream != null )
- {
- try
- {
- bundleStream.close();
- }
- catch ( IOException ignore )
- {
- }
- }
- bundleFile.delete();
- }
- }
-
-
- protected void sleepSilently( long msecs )
- {
- try
- {
- sleep( msecs );
- }
- catch ( InterruptedException ie )
- {
- // don't care
- }
- }
- }
}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallHelper.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallHelper.java
new file mode 100644
index 0000000..3a437ca
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallHelper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 1997-2009 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.felix.webconsole.internal.core;
+
+
+import java.io.File;
+import java.io.InputStream;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.service.startlevel.StartLevel;
+
+
+abstract class InstallHelper extends BaseUpdateInstallHelper
+{
+ private final BundleContext bundleContext;
+ private final String location;
+ private final int startlevel;
+ private final boolean doStart;
+
+ InstallHelper( final BundleContext bundleContext, final File bundleFile, final String location, final int startlevel,
+ final boolean doStart, final boolean refreshPackages )
+ {
+ super( "Background Install " + bundleFile, bundleFile, refreshPackages );
+
+ this.bundleContext = bundleContext;
+ this.location = location;
+ this.startlevel = startlevel;
+ this.doStart = doStart;
+ }
+
+
+ protected Bundle doRun( InputStream bundleStream ) throws BundleException
+ {
+ Bundle bundle = bundleContext.installBundle( location, bundleStream );
+
+ if ( startlevel > 0 )
+ {
+ StartLevel sl = ( StartLevel ) getService( StartLevel.class.getName() );
+ if ( sl != null )
+ {
+ sl.setBundleStartLevel( bundle, startlevel );
+ }
+ }
+
+ if ( doStart )
+ {
+ bundle.start();
+ }
+
+ return bundle;
+ }
+}
\ No newline at end of file
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateHelper.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateHelper.java
new file mode 100644
index 0000000..bcb5c7d
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateHelper.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 1997-2009 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.felix.webconsole.internal.core;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import org.apache.felix.webconsole.internal.obr.DeployerThread;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.service.log.LogService;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Resolver;
+import org.osgi.service.obr.Resource;
+
+
+abstract class UpdateHelper extends BaseUpdateInstallHelper
+{
+
+ private final Bundle bundle;
+
+
+ UpdateHelper( final Bundle bundle, boolean refreshPackages )
+ {
+ this( bundle, null, refreshPackages );
+ }
+
+
+ UpdateHelper( final Bundle bundle, final File bundleFile, boolean refreshPackages )
+ {
+ super( "Background Update " + bundle.getSymbolicName() + " (" + bundle.getBundleId() + ")", bundleFile,
+ refreshPackages );
+ this.bundle = bundle;
+ }
+
+
+ protected Bundle doRun( final InputStream bundleStream ) throws BundleException
+ {
+ bundle.update( bundleStream );
+ return bundle;
+ }
+
+
+ protected Bundle doRun() throws BundleException, IOException
+ {
+ // update the bundle from the file if defined
+ if ( getBundleFile() != null )
+ {
+ return super.doRun();
+ }
+
+ // try updating from the bundle location
+ if ( updateFromBundleLocation() )
+ {
+ return bundle;
+ }
+
+ // ensure we have a symbolic name for the OBR update to follow
+ if ( bundle.getSymbolicName() == null )
+ {
+ throw new BundleException( "Cannot update bundle: Symbolic Name is required for OBR update" );
+ }
+
+ // try updating from OBR
+ if ( updateFromOBR() )
+ {
+ return bundle;
+ }
+
+ // bundle was not updated, return nothing
+ return null;
+ }
+
+
+ private boolean updateFromBundleLocation() throws BundleException
+ {
+ final String location = bundle.getLocation();
+ getLog().log( LogService.LOG_DEBUG, "Trying to update from bundle location " + location );
+
+ InputStream input = null;
+ try
+ {
+ final URL locationURL = new URL( location );
+ input = locationURL.openStream();
+ if ( input != null )
+ {
+ doRun( input );
+ getLog().log( LogService.LOG_INFO, "Bundle updated from bundle location " + location );
+ return true;
+ }
+ }
+ catch ( IOException ioe )
+ {
+ // MalformedURLException: cannot create an URL/input for the location, use OBR
+ // IOException: cannot open stream on URL ? lets use OBR then
+ getLog().log( LogService.LOG_DEBUG, "Update failure from bundle location " + location, ioe );
+ }
+ finally
+ {
+ if ( input != null )
+ {
+ try
+ {
+ input.close();
+ }
+ catch ( IOException ignore )
+ {
+ }
+ }
+ }
+
+ // not installed from the bundle location
+ return false;
+ }
+
+
+ private boolean updateFromOBR()
+ {
+ RepositoryAdmin ra = ( RepositoryAdmin ) getService( RepositoryAdmin.class.getName() );
+ if ( ra != null )
+ {
+ getLog().log( LogService.LOG_DEBUG, "Trying to update from OSGi Bundle Repository" );
+
+ final Resolver resolver = ra.resolver();
+
+ String version = ( String ) bundle.getHeaders().get( Constants.BUNDLE_VERSION );
+ if ( version == null )
+ {
+ version = "0.0.0";
+ }
+ final String filter = "(&(symbolicname=" + bundle.getSymbolicName() + ")(!(version=" + version
+ + "))(version>=" + version + "))";
+
+ final Resource[] resources = ra.discoverResources( filter );
+ final Resource resource = selectHighestVersion( resources );
+ if ( resource != null )
+ {
+ resolver.add( resource );
+
+ if ( !resolver.resolve() )
+ {
+ DeployerThread.logRequirements( getLog(),
+ "Cannot updated bundle from OBR due to unsatisfied requirements", resolver
+ .getUnsatisfiedRequirements() );
+ }
+ else
+ {
+ DeployerThread.logResource( getLog(), "Installing Requested Resources", resolver
+ .getAddedResources() );
+ DeployerThread.logResource( getLog(), "Installing Required Resources", resolver
+ .getRequiredResources() );
+ DeployerThread.logResource( getLog(), "Installing Optional Resources", resolver
+ .getOptionalResources() );
+
+ // deploy the resolved bundles and ensure they are started
+ resolver.deploy( true );
+ getLog().log( LogService.LOG_INFO, "Bundle updated from OSGi Bundle Repository" );
+
+ return true;
+ }
+ }
+ else
+ {
+ getLog().log( LogService.LOG_INFO,
+ "Nothing to update, OSGi Bundle Repository does not provide more recent version" );
+ }
+ }
+ else
+ {
+ getLog().log( LogService.LOG_DEBUG, "Cannot updated from OSGi Bundle Repository: Service not available" );
+ }
+
+ // fallback to false, nothing done
+ return false;
+ }
+
+
+ private Resource selectHighestVersion( final Resource[] candidates )
+ {
+ if ( candidates == null || candidates.length == 0 )
+ {
+ // nothing to do if there are none
+ return null;
+ }
+ else if ( candidates.length == 1 )
+ {
+ // simple choice if there is a single one
+ return candidates[0];
+ }
+
+ // now go on looking for the highest version
+ Resource best = candidates[0];
+ for ( int i = 1; i < candidates.length; i++ )
+ {
+ if ( best.getVersion().compareTo( candidates[i].getVersion() ) < 0)
+ {
+ best = candidates[i];
+ }
+ }
+ return best;
+ }
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/DeployerThread.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/DeployerThread.java
index 0040981..8753427 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/DeployerThread.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/DeployerThread.java
@@ -58,16 +58,16 @@
if ( obrResolver.resolve() )
{
- logResource( "Installing Requested Resources", obrResolver.getAddedResources() );
- logResource( "Installing Required Resources", obrResolver.getRequiredResources() );
- logResource( "Installing Optional Resources", obrResolver.getOptionalResources() );
+ logResource( logger, "Installing Requested Resources", obrResolver.getAddedResources() );
+ logResource( logger, "Installing Required Resources", obrResolver.getRequiredResources() );
+ logResource( logger, "Installing Optional Resources", obrResolver.getOptionalResources() );
obrResolver.deploy( startBundles );
}
else
{
- logRequirements( "Cannot Install requested bundles due to unsatisfied requirements", obrResolver
- .getUnsatisfiedRequirements() );
+ logRequirements( logger, "Cannot Install requested bundles due to unsatisfied requirements",
+ obrResolver.getUnsatisfiedRequirements() );
}
}
catch ( Exception ie )
@@ -78,7 +78,7 @@
}
- private void logResource( String message, Resource[] res )
+ public static void logResource( Logger logger, String message, Resource[] res )
{
if ( res != null && res.length > 0 )
{
@@ -92,7 +92,7 @@
}
- private void logRequirements( String message, Requirement[] req )
+ public static void logRequirements( Logger logger, String message, Requirement[] req )
{
logger.log( LogService.LOG_ERROR, message );
for ( int i = 0; req != null && i < req.length; i++ )
diff --git a/webconsole/src/main/resources/res/imgs/bundle_update.png b/webconsole/src/main/resources/res/imgs/bundle_update.png
new file mode 100644
index 0000000..0746316
--- /dev/null
+++ b/webconsole/src/main/resources/res/imgs/bundle_update.png
Binary files differ
diff --git a/webconsole/src/main/resources/res/ui/admin.css b/webconsole/src/main/resources/res/ui/admin.css
index 81e13de..6bc0085 100644
--- a/webconsole/src/main/resources/res/ui/admin.css
+++ b/webconsole/src/main/resources/res/ui/admin.css
@@ -561,7 +561,7 @@
width: 50px;
}
.col_Actions {
- width: 95px;
+ width: 121px;
}
/** Added for new Install stuff */