FELIX-4734 : Web Console RESTful API should wait for asynchonous operations until they complete. Apply slightly modified patch from Valentin Valchev

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1654733 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
index fb7a60b..7dff3cd 100644
--- 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
@@ -21,12 +21,16 @@
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.felix.webconsole.SimpleWebConsolePlugin;
 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.service.log.LogService;
 import org.osgi.service.packageadmin.PackageAdmin;
 
@@ -106,9 +110,6 @@
 
     public final void run()
     {
-        // wait some time for the request to settle
-        sleepSilently( 500L );
-
         // now deploy the resolved bundles
         try
         {
@@ -118,15 +119,13 @@
             // invalid by the time we want to call the update
             PackageAdmin pa = ( refreshPackages ) ? ( PackageAdmin ) getService( PackageAdmin.class.getName() ) : null;
 
+            // perform the action!
             Bundle bundle = doRun();
 
             if ( pa != null && bundle != null )
             {
-                // wait for asynchronous bundle start tasks to finish
-                sleepSilently( 2000L );
-
-                pa.refreshPackages( new Bundle[]
-                    { bundle } );
+                // refresh packages and give it at most 5 seconds to finish
+                refreshPackages( pa, plugin.getBundle().getBundleContext(), 5000L, bundle );
             }
         }
         catch ( Exception e )
@@ -158,15 +157,97 @@
     }
 
 
-    protected void sleepSilently( long msecs )
+    /**
+     * This is an utility method that issues refresh package instruction to the framework.
+     *
+     * @param packageAdmin is the package admin service obtained using the bundle context of the caller.
+     *   If this is <code>null</code> no refresh packages is performed
+     * @param bundleContext of the caller. This is needed to add a framework listener.
+     * @param maxWait the maximum time to wait for the packages to be refreshed
+     * @param bundle the bundle, which packages to refresh or <code>null</code> to refresh all packages.
+     * @return true if refresh is succesfull within the given time frame
+     */
+    static boolean refreshPackages(final PackageAdmin packageAdmin,
+        final BundleContext bundleContext,
+        final long maxWait,
+        final Bundle bundle)
     {
-        try
+        return new RefreshPackageTask().refreshPackages( packageAdmin, bundleContext, maxWait, bundle );
+    }
+
+    static class RefreshPackageTask implements FrameworkListener
+    {
+
+        private volatile boolean refreshed = false;
+        private final Object lock = new Object();
+
+        boolean refreshPackages( final PackageAdmin packageAdmin,
+            final BundleContext bundleContext,
+            final long maxWait,
+            final Bundle bundle )
         {
-            Thread.sleep( msecs );
+            if (null == packageAdmin)
+            {
+                return false;
+            }
+
+            if (null == bundleContext)
+            {
+                return false;
+            }
+
+            bundleContext.addFrameworkListener(this);
+
+            if (null == bundle)
+            {
+                packageAdmin.refreshPackages(null);
+            }
+            else
+            {
+                packageAdmin.refreshPackages(new Bundle[] { bundle });
+            }
+
+            try
+            {
+                // check for spurious wait
+                long start = System.currentTimeMillis();
+                long delay = maxWait;
+                while (!refreshed && delay > 0)
+                {
+                    synchronized (lock)
+                    {
+                        if ( !refreshed )
+                        {
+                            lock.wait(delay);
+                            // remove the time we already waited for
+                            delay = maxWait - (System.currentTimeMillis() - start);
+                        }
+                    }
+                }
+            }
+            catch (final InterruptedException e)
+            {
+                // just return if the thread is interrupted
+                Thread.currentThread().interrupt();
+            }
+            finally
+            {
+                bundleContext.removeFrameworkListener(this);
+            }
+            return refreshed;
         }
-        catch ( InterruptedException ie )
+
+        public void frameworkEvent(final FrameworkEvent e)
         {
-            // don't care
+            if (e.getType() == FrameworkEvent.PACKAGES_REFRESHED)
+            {
+                synchronized (lock)
+                {
+                    refreshed = true;
+                    lock.notifyAll();
+                }
+            }
+
         }
     }
 }
\ 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 605fb09..3bd0461 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
@@ -327,7 +327,8 @@
         final String action = WebConsoleUtil.getParameter( req, "action" );
         if ( "refreshPackages".equals( action ) )
         {
-            getPackageAdmin().refreshPackages( null );
+            // refresh packages and give it most 15 seconds to finish
+            BaseUpdateInstallHelper.refreshPackages( getPackageAdmin(), getBundleContext(), 15000L, null );
             success = true;
         }
         else if ( "install".equals( action ) )
@@ -384,8 +385,8 @@
                 }
                 else if ( "refresh".equals( action ) )
                 {
-                    // refresh bundle wiring
-                    refresh( bundle );
+                    // refresh bundle wiring and give at most 5 seconds to finish
+                    BaseUpdateInstallHelper.refreshPackages( getPackageAdmin(), getBundleContext(), 5000L, bundle );
                 }
                 else if ( "update".equals( action ) )
                 {
@@ -406,14 +407,6 @@
                     }
                 }
 
-                // let's wait a little bit to give the framework time
-                // to process our request
-                try {
-                    Thread.sleep(800);
-                } catch (InterruptedException e) {
-                    // we ignore this
-                }
-
                 // write the state only
                 resp.setContentType( "application/json" ); //$NON-NLS-1$
                 resp.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
@@ -433,13 +426,6 @@
 
         if ( success && null != getBundleContext() )
         {
-            // let's wait a little bit to give the framework time
-            // to process our request
-            try {
-                Thread.sleep(800);
-            } catch (InterruptedException e) {
-                // we ignore this
-            }
             final String pluginRoot = ( String ) req.getAttribute( WebConsoleConstants.ATTR_PLUGIN_ROOT );
             final String servicesRoot = getServicesRoot( req );
             try
@@ -1456,14 +1442,6 @@
         return val.toString();
     }
 
-
-    private void refresh( final Bundle bundle )
-    {
-        getPackageAdmin().refreshPackages( new Bundle[]
-            { bundle } );
-    }
-
-
     private void update( final Bundle bundle )
     {
         UpdateHelper t = new UpdateHelper( this, bundle, false );