You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stanbol.apache.org by rw...@apache.org on 2012/03/09 09:57:07 UTC

svn commit: r1298756 - /incubator/stanbol/trunk/commons/installer/bundleprovider/src/main/java/org/apache/stanbol/commons/installer/provider/bundle/impl/BundleInstaller.java

Author: rwesten
Date: Fri Mar  9 08:57:07 2012
New Revision: 1298756

URL: http://svn.apache.org/viewvc?rev=1298756&view=rev
Log:
Major improvements to the BundleInstaller (Sling Installer Provider plugin for installing resources provided by OSGI bundles)

* fixes STANBOL-530: Resources are now correctly uninstalled even if the Bundle is already in the UNINSTALLED state and Resources within the Bundle are no longer accessible
* fixes STANBOL-531: the ID of resources is now correctly parsed even if the configured  Install-Path is also contained within the name of the resource
* implements STANBOL-532: Resources are once again uninstalled if Bundles are STOPPED. However the BundleInstaller now checks if those events are caused by a User or the OSGI environment shutting down.  

Modified:
    incubator/stanbol/trunk/commons/installer/bundleprovider/src/main/java/org/apache/stanbol/commons/installer/provider/bundle/impl/BundleInstaller.java

Modified: incubator/stanbol/trunk/commons/installer/bundleprovider/src/main/java/org/apache/stanbol/commons/installer/provider/bundle/impl/BundleInstaller.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/trunk/commons/installer/bundleprovider/src/main/java/org/apache/stanbol/commons/installer/provider/bundle/impl/BundleInstaller.java?rev=1298756&r1=1298755&r2=1298756&view=diff
==============================================================================
--- incubator/stanbol/trunk/commons/installer/bundleprovider/src/main/java/org/apache/stanbol/commons/installer/provider/bundle/impl/BundleInstaller.java (original)
+++ incubator/stanbol/trunk/commons/installer/bundleprovider/src/main/java/org/apache/stanbol/commons/installer/provider/bundle/impl/BundleInstaller.java Fri Mar  9 08:57:07 2012
@@ -19,24 +19,33 @@ package org.apache.stanbol.commons.insta
 import static org.apache.stanbol.commons.installer.provider.bundle.BundleInstallerConstants.BUNDLE_INSTALLER_HEADER;
 import static org.apache.stanbol.commons.installer.provider.bundle.BundleInstallerConstants.PROVIDER_SCHEME;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
 import org.apache.sling.installer.api.InstallableResource;
 import org.apache.sling.installer.api.OsgiInstaller;
 import org.apache.sling.installer.api.tasks.ResourceTransformer;
 import org.apache.stanbol.commons.installer.provider.bundle.BundleInstallerConstants;
+import org.osgi.application.Framework;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
 import org.osgi.framework.BundleListener;
 import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.SynchronousBundleListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -96,7 +105,10 @@ public class BundleInstaller implements 
      */
     private final OsgiInstaller installer;
     private final BundleContext context;
-
+    /**
+     * The directory used to keep the IDs of registered resources
+     */
+    File configDir;
     /**
      * contains all active bundles as key and the path to the config directory
      * as value. A <code>null</code> value indicates that this bundle needs not
@@ -113,11 +125,35 @@ public class BundleInstaller implements 
         }
         this.installer = installer;
         this.context = context;
+        this.configDir = context.getDataFile(".config");
+        if(configDir.exists()) {
+            if(!configDir.isDirectory()){
+                throw new IllegalStateException("The config directory '" 
+                    + configDir.getAbsolutePath()+"' exists but is NOT a directory!");
+            }
+        } else {
+            if(!configDir.mkdirs()){
+                throw new IllegalStateException("Unable to create the config directory '" 
+                        + configDir.getAbsolutePath()+"'!");
+            }
+        }
+        //start with the assumption that the framework is active
         this.context.addBundleListener(this);
         //register the already active bundles
         registerActive(this.context);
+        
     }
-
+    /**
+     * Checks if the state is not {@link BundleEvent#STOPPED}, 
+     * {@link BundleEvent#STOPPING} or {@link BundleEvent#UNINSTALLED}
+     * @return the state
+     */
+    private boolean isFrameworkActive(){
+        return (context.getBundle(0).getState() & 
+            (BundleEvent.STOPPED | BundleEvent.STOPPING | BundleEvent.UNINSTALLED))
+            == 0;
+    }
+    
     /**
      * Uses the parsed bundle context to register the already active (and currently
      * starting) bundles.
@@ -132,22 +168,24 @@ public class BundleInstaller implements 
 
     @Override
     public void bundleChanged(BundleEvent event) {
+        log.debug("bundleChanged(bundle:{}|state:{})",event.getBundle().getSymbolicName(),event.getType());
+        Bundle source = event.getBundle();
+        //if(event instanceof Framework)
         switch (event.getType()) {
             case BundleEvent.STARTED:
-                register(event.getBundle());
+                register(source);
                 break;
             //use uninstalled instead of stopped so that unregister is not called
             //when the OSGI environment closes
-            case BundleEvent.UNINSTALLED://STOPPED:
-                unregister(event.getBundle());
+            case BundleEvent.STOPPED:
+                unregister(source);
                 break;
 
             case BundleEvent.UPDATED:
-                unregister(event.getBundle());
-                register(event.getBundle());
+                unregister(source);
+                register(source);
         }
     }
-
     /**
      * Registers the bundle to the {@link #activated} map.
      *
@@ -155,12 +193,18 @@ public class BundleInstaller implements 
      */
     @SuppressWarnings("unchecked")
     private void register(Bundle bundle) {
+        log.debug("register request for Bundle {}",bundle.getSymbolicName());
         synchronized (activated) {
+            if(!isFrameworkActive()){
+                log.debug("ignore because Framework is shutting down!");
+                return;
+            }
             if (activated.containsKey(bundle)) {
+                log.debug("  .. already registered ");
                 return;
             }
         }
-        log.debug("Register Bundle {} with BundleInstaller",bundle.getSymbolicName());
+        log.debug("  ... registering");
         Dictionary<String, Object> headers = (Dictionary<String, Object>) bundle.getHeaders();
         //        log.info("With Headers:");
         //        for(Enumeration<String> keys = headers.keys();keys.hasMoreElements();){
@@ -184,6 +228,13 @@ public class BundleInstaller implements 
                         }
                     }
                 }
+                try {
+                    storeConfig(bundle, updated);
+                } catch (IOException e) {
+                    throw new IllegalStateException("Unablt to save the IDs of" 
+                    		+ "the resources installed for bundle '"
+                            + bundle.getSymbolicName() + "'!",e);
+                }
                 installer.updateResources(PROVIDER_SCHEME, updated.toArray(new InstallableResource[updated.size()]), new String[]{});
             } else {
                 log.warn(" ... no Entries found in path '{}' configured for Bundle '{}' with Manifest header field '{}'!",
@@ -193,7 +244,55 @@ public class BundleInstaller implements 
             log.debug("  ... no Configuration to process");
         }
     }
-
+    /**
+     * Used to stores/overrides the ids of installed resource for a bundle.
+     * @param bundle
+     * @param resources
+     * @throws IOException
+     */
+    private void storeConfig(Bundle bundle,Collection<InstallableResource> resources) throws IOException{
+        synchronized (configDir) {
+            File config = new File(configDir,bundle.getBundleId()+".resources");
+            if(config.exists()){
+                config.delete();
+            }
+            FileOutputStream out = new FileOutputStream(config);
+            List<String> ids = new ArrayList<String>(resources.size());
+            for(InstallableResource resource : resources){
+                ids.add(resource.getId());
+            }
+            try {
+                IOUtils.writeLines(ids, null, out, "UTF-8");
+            } finally {
+                IOUtils.closeQuietly(out);
+            }
+        }
+    }
+    /**
+     * Reads the installed resources for the parsed bundle and deletes the
+     * configuration afterwards.
+     * @param bundle the bundle
+     * @return the list of resources
+     * @throws IOException if the config file was not found or on any other
+     * exception while reading the file
+     */
+    private Collection<String> consumeConfig(Bundle bundle) throws IOException{
+        synchronized (configDir) {
+            File config = new File(configDir,bundle.getBundleId()+".resources");
+            if(!config.exists()){
+                throw new IOException("Configuration File '"+ config.getAbsolutePath()
+                    + "' not found!");
+            }
+            FileInputStream in = new FileInputStream(config);
+            try {
+                return IOUtils.readLines(in, "UTF-8");
+            } finally {
+                IOUtils.closeQuietly(in);
+                config.delete();
+            }
+        }
+    }
+    
     /**
      * Creates an {@link InstallableResource} for {@link URL}s of files within
      * the parsed bundle.
@@ -242,30 +341,42 @@ public class BundleInstaller implements 
      */
     private String getInstallableResourceId(String path, URL bundleResource) {
         String id = bundleResource.toString();
-        String relPath = id.substring(id.lastIndexOf(path) + path.length(), id.length());
+        String namespace; //do not search the path within the file name!!
+        int nsIndex = Math.max(id.lastIndexOf(':'), id.lastIndexOf(File.separatorChar));
+        if(nsIndex > 0){
+            namespace = id.substring(0,Math.min(nsIndex+1,id.length()));
+        } else {
+            namespace = id;
+        }
+        String relPath = id.substring(namespace.lastIndexOf(path) + path.length(), id.length());
         return relPath;
     }
 
-    @SuppressWarnings("unchecked")
     private void unregister(Bundle bundle) {
+        log.debug("unregister request for Bundle {}",bundle.getSymbolicName());
         String path;
         synchronized (activated) {
+            if(!isFrameworkActive()){
+                log.debug("ignore because Framework is shutting down!");
+                return;
+            }
             if (!activated.containsKey(bundle)) {
+                log.debug("  .. not registered ");
                 return;
             }
             path = activated.remove(bundle);
         }
         if (path != null) {
-            log.info(" ... remove configurations within path {}", path);
-            ArrayList<String> removedResources = new ArrayList<String>();
-            for (Enumeration<URL> resources = (Enumeration<URL>) bundle.findEntries(path, null, true); resources.hasMoreElements();) {
-                String installableResourceId = getInstallableResourceId(path, resources.nextElement());
-                if (installableResourceId != null) {
-                    log.info("  ... remove Installable Resource {}",installableResourceId);
-                    removedResources.add(installableResourceId);
-                }
+            log.info(" ... remove configurations for Bundle {}", bundle.getSymbolicName());
+            Collection<String> removedResources;
+            try {
+                removedResources = consumeConfig(bundle);
+                installer.updateResources(PROVIDER_SCHEME, null, removedResources.toArray(new String[removedResources.size()]));
+            } catch (IOException e) {
+                log.warn("Unable to remove installed Resources for Bundle '" +
+                		bundle.getSymbolicName()+ "' because an Exeption while" +
+                				"reading the installed ID from the config file",e);
             }
-            installer.updateResources(PROVIDER_SCHEME, null, removedResources.toArray(new String[removedResources.size()]));
         } else {
             log.debug("  ... no Configuration to process");
         }