You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2009/08/17 21:36:18 UTC

svn commit: r805111 - in /sling/trunk/installer/osgi: installer/src/main/java/org/apache/sling/osgi/installer/ installer/src/main/java/org/apache/sling/osgi/installer/impl/ installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/ installer/s...

Author: bdelacretaz
Date: Mon Aug 17 19:36:18 2009
New Revision: 805111

URL: http://svn.apache.org/viewvc?rev=805111&view=rev
Log:
SLING-1078 - BundleTaskCreator and tests, work in progress

Added:
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java   (with props)
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java   (with props)
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleUpdateTask.java   (with props)
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/BundleTaskCreatorTest.java   (with props)
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleResource.java   (with props)
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleTaskCreator.java   (with props)
Modified:
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/BundleTaskCreator.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerThread.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceComparator.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallRemoveTask.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/TaskOrder.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/TaskUtilities.java
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/DictionaryConversionTest.java
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/DictionaryDigestTest.java
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/LocalFileRegisteredResource.java
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceTest.java
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/TaskOrderingTest.java
    sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/BundleInstallTest.java

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java Mon Aug 17 19:36:18 2009
@@ -61,6 +61,9 @@
 	/** Counter index: number of OSGi tasks executed */
 	int OSGI_TASKS_COUNTER = 0;
 	
+    /** Counter index: number of installer cycles */
+    int INSTALLER_CYCLES_COUNTER = 1;
+    
 	/** Size of the counters array */
-	int COUNTERS_SIZE = 1;
+	int COUNTERS_SIZE = 2;
 }
\ No newline at end of file

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/BundleTaskCreator.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/BundleTaskCreator.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/BundleTaskCreator.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/BundleTaskCreator.java Mon Aug 17 19:36:18 2009
@@ -18,16 +18,90 @@
  */
 package org.apache.sling.osgi.installer.impl;
 
-import java.util.TreeSet;
+import java.util.SortedSet;
 
 import org.apache.sling.osgi.installer.impl.tasks.BundleInstallTask;
+import org.apache.sling.osgi.installer.impl.tasks.BundleRemoveTask;
+import org.apache.sling.osgi.installer.impl.tasks.BundleUpdateTask;
+import org.apache.sling.osgi.installer.impl.tasks.TaskUtilities;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
 
 /** TaskCreator that processes a list of bundle RegisteredResources */
 class BundleTaskCreator implements OsgiInstallerThread.TaskCreator {
 
-	public void createTasks(TreeSet<RegisteredResource> resources, TreeSet<OsgiInstallerTask> tasks) {
+    public static final String MAVEN_SNAPSHOT_MARKER = "SNAPSHOT";
+    
+    /** Holds the bundle info that we need, makes it easier to test
+     *  without an OSGi framework */ 
+    static class BundleInfo {
+        final Version version;
+        final int state;
+        
+        BundleInfo(Version version, int state) {
+            this.version = version;
+            this.state = state;
+        }
+        
+        BundleInfo(Bundle b) {
+            this.version = new Version((String)b.getHeaders().get(Constants.BUNDLE_VERSION));
+            this.state = b.getState();
+        }
+    }
+    
+	/** Create tasks for a set of RegisteredResource that all represent the same bundle.
+	 * 	Selects the bundle with the highest priority (i.e. the first one in the group that
+	 *  has desired state == active, and generates the appropriate OSGi tasks to
+	 *  reach this state. 
+	 */
+	public void createTasks(BundleContext ctx, SortedSet<RegisteredResource> resources, SortedSet<OsgiInstallerTask> tasks) {
+		
+		// Find the bundle that must be active: the resources collection is ordered according
+		// to priorities, so we just need to find the first one that is installable
+		RegisteredResource toActivate = null;
 		for(RegisteredResource r : resources) {
-			tasks.add(new BundleInstallTask(r));
+			if(toActivate == null && r.isInstallable()) {
+				toActivate = r;
+				break;
+			}
 		}
+
+		if(toActivate == null) {
+		    // None of our resources are installable, remove corresponding bundle
+		    final BundleInfo info = getBundleInfo(ctx, resources.first());
+		    if(info != null) {
+	            tasks.add(new BundleRemoveTask(resources.first()));
+	        }
+			
+		} else {
+			final BundleInfo info = getBundleInfo(ctx, toActivate);
+			final Version newVersion = (Version)toActivate.getAttributes().get(Constants.BUNDLE_VERSION);
+			if(info == null) {
+			    // bundle is not installed yet
+				tasks.add(new BundleInstallTask(toActivate));
+			} else {
+			    final int compare = info.version.compareTo(newVersion); 
+			    if(compare != 0) {
+	                // installed but different version. Can be a later version if 
+			        // the newer version resource was removed, in case we downgrade
+	                tasks.add(new BundleUpdateTask(toActivate));
+			    } else if(compare == 0 && newVersion.toString().indexOf(MAVEN_SNAPSHOT_MARKER) >= 0){
+			        // installed, same version but SNAPSHOT
+			        // TODO: update only if not updated with same digest yet
+			        tasks.add(new BundleUpdateTask(toActivate));
+			    }
+			}
+		}
+	}
+
+	protected BundleInfo getBundleInfo(BundleContext ctx, RegisteredResource bundle) {
+		final String symbolicName = (String)bundle.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME);
+		final Bundle b = TaskUtilities.getMatchingBundle(ctx, symbolicName);
+		if(b == null) {
+		    return null;
+        }
+		return new BundleInfo(b);
 	}
 }

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java Mon Aug 17 19:36:18 2009
@@ -47,7 +47,12 @@
         this.packageAdmin = pa;
         this.logServiceTracker = logServiceTracker;
         
-        installerThread = new OsgiInstallerThread(this);
+        installerThread = new OsgiInstallerThread(this) {
+            @Override
+            protected void cycleDone() {
+                incrementCounter(INSTALLER_CYCLES_COUNTER);
+            }
+        };
         installerThread.setDaemon(true);
         installerThread.start();
     }
@@ -102,7 +107,7 @@
 
 	public void addResource(InstallableResource r) throws IOException {
 	    // TODO do not add if we already have it, based on digest
-	    installerThread.addNewResource(new RegisteredResource(bundleContext, r));
+	    installerThread.addNewResource(new RegisteredResourceImpl(bundleContext, r));
 	}
 
 	public void registerResources(Collection<InstallableResource> data,
@@ -110,8 +115,10 @@
 		// TODO
 	}
 
-	public void removeResource(InstallableResource d) throws IOException {
-		// TODO
+	public void removeResource(InstallableResource r) throws IOException {
+		final RegisteredResource rr = new RegisteredResourceImpl(bundleContext, r);
+		rr.setInstallable(false);
+		installerThread.addNewResource(rr);
 	}
 
 	public Storage getStorage() {

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerThread.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerThread.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerThread.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerThread.java Mon Aug 17 19:36:18 2009
@@ -22,8 +22,11 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
 import java.util.TreeSet;
 
+import org.osgi.framework.BundleContext;
 import org.osgi.service.log.LogService;
 
 /** Worker thread where all OSGi tasks are executed.
@@ -38,21 +41,23 @@
     
     private final OsgiInstallerContext ctx;
     private final List<RegisteredResource> newResources = new LinkedList<RegisteredResource>();
-    private final TreeSet<OsgiInstallerTask> tasks = new TreeSet<OsgiInstallerTask>();
+    private final SortedSet<OsgiInstallerTask> tasks = new TreeSet<OsgiInstallerTask>();
     
     /** Group our RegisteredResource by OSGi entity */ 
-    private final Map<String, TreeSet<RegisteredResource>> registeredResources = 
-    	new HashMap<String, TreeSet<RegisteredResource>>();
+    private Map<String, SortedSet<RegisteredResource>>registeredResources = 
+        new HashMap<String, SortedSet<RegisteredResource>>();
+    
+    private final BundleTaskCreator bundleTaskCreator = new BundleTaskCreator();
     
     static interface TaskCreator {
     	/** Add the required OsgiInstallerTasks to the tasks collection, so that the resources reach
     	 * 	their desired states.
+    	 *  @param ctx used to find out which bundles and configs are currently active
     	 * 	@param resources ordered set of RegisteredResource which all have the same entityId
     	 * 	@param tasks lists of tasks, to which we'll add the ones computed by this method
     	 */
-    	void createTasks(TreeSet<RegisteredResource> resources, TreeSet<OsgiInstallerTask> tasks);
+    	void createTasks(BundleContext ctx, SortedSet<RegisteredResource> resources, SortedSet<OsgiInstallerTask> tasks);
     }
-    private final TaskCreator bundleTaskCreator = new BundleTaskCreator();
     
     OsgiInstallerThread(OsgiInstallerContext ctx) {
         setName(getClass().getSimpleName());
@@ -65,9 +70,10 @@
             // TODO do nothing if nothing to process!
             try {
             	mergeNewResources();
-                computeTasks();
+            	computeTasks();
                 executeTasks();
                 Thread.sleep(250);
+                cycleDone();
             } catch(Exception e) {
                 if(ctx.getLogService() != null) {
                     ctx.getLogService().log(LogService.LOG_WARNING, e.toString(), e);
@@ -96,33 +102,35 @@
         }
     }
     
-    private void addRegisteredResource(RegisteredResource r) {
-        TreeSet<RegisteredResource> t = registeredResources.get(r.getEntityId());
-        if(t == null) {
-            t = new TreeSet<RegisteredResource>(new RegisteredResourceComparator());
-            registeredResources.put(r.getEntityId(), t);
-        }
-        t.add(r);
-
-    }
-    
     private void mergeNewResources() {
         synchronized (newResources) {
             for(RegisteredResource r : newResources) {
-            	addRegisteredResource(r);
+                SortedSet<RegisteredResource> t = registeredResources.get(r.getEntityId());
+                if(t == null) {
+                    t = createRegisteredResourcesEntry();
+                    registeredResources.put(r.getEntityId(), t);
+                }
+                t.add(r);
             }
             newResources.clear();
         }
     }
+
+    /** Factored out to use the exact same structure in tests */
+    static SortedSet<RegisteredResource> createRegisteredResourcesEntry() {
+        return new TreeSet<RegisteredResource>(new RegisteredResourceComparator());
+    }
+    
     
-    private void computeTasks() {
+    /** Compute OSGi tasks based on our resources, and add to supplied list of tasks */
+    void computeTasks() {
         // Walk the list of entities, and create appropriate OSGi tasks for each group
-        for(TreeSet<RegisteredResource> group : registeredResources.values()) {
-        	if(group.first().getResourceType().equals(RegisteredResource.ResourceType.BUNDLE)) {
-        		bundleTaskCreator.createTasks(group, tasks);
-        	} else {
-        		throw new IllegalArgumentException("No TaskCreator for resource type "+ group.first().getResourceType());
-        	} 
+        for(SortedSet<RegisteredResource> group : registeredResources.values()) {
+            if(group.first().getResourceType().equals(RegisteredResource.ResourceType.BUNDLE)) {
+                bundleTaskCreator.createTasks(ctx.getBundleContext(), group, tasks);
+            } else {
+                throw new IllegalArgumentException("No TaskCreator for resource type "+ group.first().getResourceType());
+            } 
         }
     }
     
@@ -139,4 +147,6 @@
         }
     }
     
+    protected void cycleDone() {
+    }   
 }
\ No newline at end of file

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java Mon Aug 17 19:36:18 2009
@@ -18,297 +18,45 @@
  */
 package org.apache.sling.osgi.installer.impl;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.List;
 import java.util.Map;
-import java.util.Properties;
-import java.util.jar.JarInputStream;
-import java.util.jar.Manifest;
-
-import org.apache.sling.osgi.installer.InstallableResource;
-import org.apache.sling.osgi.installer.impl.propertyconverter.PropertyConverter;
-import org.apache.sling.osgi.installer.impl.propertyconverter.PropertyValue;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
 
 /** A resource that's been registered in the OSGi controller.
  * 	Data can be either an InputStream or a Dictionary, and we store
  *  it locally to avoid holding up to classes or data from our 
  *  clients, in case those disappear while we're installing stuff. 
  */
-public class RegisteredResource {
-	private final String url;
-	private final String digest;
-	private final File dataFile;
-	private final String entity;
-	private final Dictionary<String, Object> dictionary;
-	private final Manifest manifest;
-	private static long fileNumber;
-	
-	static enum State {
-	    NEW,
-	    ACTIVE,
-        INSTALLED,
-	    IGNORED,
-	    REMOVED,
-	}
-    private State desiredState = State.ACTIVE;
-    private State actualState = State.NEW;
-    
-    static enum ResourceType {
+public interface RegisteredResource {
+
+	static enum ResourceType {
         BUNDLE,
         CONFIG
     }
     
-    private final ResourceType resourceType;
-	
 	public static final String DIGEST_TYPE = "MD5";
     public static final String ENTITY_JAR_PREFIX = "jar:";
 	public static final String ENTITY_BUNDLE_PREFIX = "bundle:";
 	public static final String ENTITY_CONFIG_PREFIX = "config:";
 	
-	/** Create a RegisteredResource from given data. If the data's extension
-	 *  maps to a configuration and the data provides an input stream, it is
-	 *  converted to a Dictionary 
-	 */
-	public RegisteredResource(BundleContext ctx, InstallableResource input) throws IOException {
-	    
-	    try {
-    		url = input.getUrl();
-    		resourceType = computeResourceType(input.getExtension());
-    		
-    		if(resourceType == ResourceType.BUNDLE) {
-                if(input.getInputStream() == null) {
-                    throw new IllegalArgumentException("InputStream is required for BUNDLE resource type: " + input);
-                }
-                dictionary = null;
-                dataFile = getDataFile(ctx);
-                copyToLocalStorage(input.getInputStream(), dataFile);
-                digest = input.getDigest();
-                if(digest == null || digest.length() == 0) {
-                    throw new IllegalArgumentException(
-                            "Digest must be supplied for BUNDLE resource type: " + input);
-                }
-                manifest = getManifest(getInputStream());
-                String name = null;
-                if(manifest != null) {
-                    name = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
-                }
-                if(name == null) {
-                    // not a bundle - use "jar" entity to make it easier to find out
-                    entity = ENTITY_JAR_PREFIX + input.getUrl();
-                } else {
-                    entity = ENTITY_BUNDLE_PREFIX + name;
-                }
-    		} else {
-                dataFile = null;
-                manifest = null;
-                entity = ENTITY_CONFIG_PREFIX + new ConfigurationPid(input.getUrl()).getCompositePid();
-                if(input.getInputStream() == null) {
-                    // config provided as a Dictionary
-                    dictionary = copy(input.getDictionary());
-                } else {
-                    dictionary = readDictionary(input.getInputStream()); 
-                }
-                try {
-                    digest = computeDigest(dictionary);
-                } catch(NoSuchAlgorithmException nse) {
-                    throw new IOException("NoSuchAlgorithmException:" + DIGEST_TYPE);
-                }
-    		}
-    	} finally {
-    		if(input.getInputStream() != null) {
-    			input.getInputStream().close();
-    		}
-    	}
-	}
-	
-	protected File getDataFile(BundleContext ctx) throws IOException {
-		String filename = null;
-		synchronized (getClass()) {
-			filename = getClass().getSimpleName() + "." + fileNumber++;
-		}
-		return ctx.getDataFile(filename);
-	}
-	
-	public void cleanup() {
-		if(dataFile != null && dataFile.exists()) {
-			dataFile.delete();
-		}
-	}
-	
-	public String getURL() {
-		return url;
-	}
-	
-	public InputStream getInputStream() throws IOException {
-		if(dataFile == null) {
-			return null;
-		}
-		return new BufferedInputStream(new FileInputStream(dataFile));
-	}
-	
-	public Dictionary<String, Object> getDictionary() {
-		return dictionary;
-	}
+	void cleanup();
+	String getURL();
+	InputStream getInputStream() throws IOException;
+	Dictionary<String, Object> getDictionary();
+	String getDigest();
+	String getUrl();
+	boolean isInstallable();
+	void setInstallable(boolean installable);
+	ResourceType getResourceType();
 	
-	public String getDigest() {
-		return digest;
-	}
+	/** Attributes include the bundle symbolic name, bundle version, etc. */
+	Map<String, Object> getAttributes();
 	
-    /** Digest is needed to detect changes in data 
-     * @throws  */
-    static String computeDigest(Dictionary<String, Object> data) throws IOException, NoSuchAlgorithmException {
-        final MessageDigest d = MessageDigest.getInstance(DIGEST_TYPE);
-        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        final ObjectOutputStream oos = new ObjectOutputStream(bos);
-        oos.writeObject(data);
-        bos.flush();
-        d.update(bos.toByteArray());
-        return digestToString(d);
-    }
-
-    /** convert digest to readable string (http://www.javalobby.org/java/forums/t84420.html) */
-    private static String digestToString(MessageDigest d) {
-        final BigInteger bigInt = new BigInteger(1, d.digest());
-        return new String(bigInt.toString(16));
-    }
-    
-    /** Copy data to local storage */
-	private void copyToLocalStorage(InputStream data, File f) throws IOException {
-		final OutputStream os = new BufferedOutputStream(new FileOutputStream(f));
-		try {
-			final byte[] buffer = new byte[16384];
-			int count = 0;
-			while( (count = data.read(buffer, 0, buffer.length)) > 0) {
-				os.write(buffer, 0, count);
-			}
-			os.flush();
-		} finally {
-			if(os != null) {
-				os.close();
-			}
-		}
-	}
-	
-	/** Convert InputStream to Dictionary using our extended properties format,
-	 * 	which supports multi-value properties 
-	 */
-	static Dictionary<String, Object> readDictionary(InputStream is) throws IOException {
-		final Dictionary<String, Object> result = new Hashtable<String, Object>();
-		final PropertyConverter converter = new PropertyConverter();
-		final Properties p = new Properties();
-        p.load(is);
-        for(Map.Entry<Object, Object> e : p.entrySet()) {
-            final PropertyValue v = converter.convert((String)e.getKey(), (String)e.getValue());
-            result.put(v.getKey(), v.getValue());
-        }
-        return result;
-	}
-	
-	/** Copy given Dictionary, sorting keys */
-	static Dictionary<String, Object> copy(Dictionary<String, Object> d) {
-	    final Dictionary<String, Object> result = new Hashtable<String, Object>();
-	    final List<String> keys = new ArrayList<String>();
-	    final Enumeration<String> e = d.keys();
-	    while(e.hasMoreElements()) {
-	        keys.add(e.nextElement());
-	    }
-	    Collections.sort(keys);
-	    for(String key : keys) {
-	        result.put(key, d.get(key));
-	    }
-	    return result;
-	}
-	
-	public String getUrl() {
-	    return url;
-	}
-
-    public State getDesiredState() {
-        return desiredState;
-    }
-
-    public void setDesiredState(State desiredState) {
-        this.desiredState = desiredState;
-    }
-
-    public State getActualState() {
-        return actualState;
-    }
-
-    public void setActualState(State actualState) {
-        this.actualState = actualState;
-    }
-    
-    public ResourceType getResourceType() {
-        return resourceType;
-    }
-    
-    public Manifest getManifest() {
-        return manifest;
-    }
-    
-    static ResourceType computeResourceType(String extension) {
-        if(extension.equals("jar")) {
-            return ResourceType.BUNDLE;
-        } else {
-            return ResourceType.CONFIG;
-        }
-    }
-    
-    /** Return the identifier of the OSGi "entity" that this resource
+	/** Return the identifier of the OSGi "entity" that this resource
      *  represents, for example "bundle:SID" where SID is the bundle's
      *  symbolic ID, or "config:PID" where PID is config's PID. 
      */
-    public String getEntityId() {
-        return entity;
-    }
+    String getEntityId();
     
-    /** Read the manifest from supplied input stream, which is closed before return */
-    static Manifest getManifest(InputStream ins) throws IOException {
-        Manifest result = null;
-
-        JarInputStream jis = null;
-        try {
-            jis = new JarInputStream(ins);
-            result= jis.getManifest();
-
-        } finally {
-
-            // close the jar stream or the inputstream, if the jar
-            // stream is set, we don't need to close the input stream
-            // since closing the jar stream closes the input stream
-            if (jis != null) {
-                try {
-                    jis.close();
-                } catch (IOException ignore) {
-                }
-            } else {
-                try {
-                    ins.close();
-                } catch (IOException ignore) {
-                }
-            }
-        }
-
-        return result;
-    }
 }

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceComparator.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceComparator.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceComparator.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceComparator.java Mon Aug 17 19:36:18 2009
@@ -20,12 +20,37 @@
 
 import java.util.Comparator;
 
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
 /** Comparator that defines priorities between RegisteredResources */
 class RegisteredResourceComparator implements Comparator<RegisteredResource >{
 
     public int compare(RegisteredResource a, RegisteredResource b) {
-        // TODO 
-        return 0;
+        if(a.getResourceType() == RegisteredResource.ResourceType.BUNDLE) {
+            return compareBundles(a, b);
+        } else {
+            return compareConfig(a, b);
+        }
+    }
+    
+    int compareBundles(RegisteredResource a, RegisteredResource b) {
+        
+        final String nameA = (String)a.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME);
+        final String nameB = (String)a.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME);
+        int result = nameA.compareTo(nameB);
+        
+        if(result == 0) {
+            final Version va = (Version)a.getAttributes().get(Constants.BUNDLE_VERSION);
+            final Version vb = (Version)b.getAttributes().get(Constants.BUNDLE_VERSION);
+            // higher version has more priority, must come first so invert comparison
+            result = vb.compareTo(va);
+        }
+        
+        return result;
     }
     
+    int compareConfig(RegisteredResource a, RegisteredResource b) {
+        return 0;
+    }
 }

Added: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java?rev=805111&view=auto
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java (added)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java Mon Aug 17 19:36:18 2009
@@ -0,0 +1,304 @@
+/*
+ * 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.sling.osgi.installer.impl;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.apache.sling.osgi.installer.InstallableResource;
+import org.apache.sling.osgi.installer.impl.propertyconverter.PropertyConverter;
+import org.apache.sling.osgi.installer.impl.propertyconverter.PropertyValue;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+/** A resource that's been registered in the OSGi controller.
+ * 	Data can be either an InputStream or a Dictionary, and we store
+ *  it locally to avoid holding up to classes or data from our 
+ *  clients, in case those disappear while we're installing stuff. 
+ */
+public class RegisteredResourceImpl implements RegisteredResource { 
+	private final String url;
+	private final String digest;
+	private final File dataFile;
+	private final String entity;
+	private final Dictionary<String, Object> dictionary;
+	private final Map<String, Object> attributes = new HashMap<String, Object>();
+	private static long fileNumber;
+	private boolean installable = true;
+	
+    static enum ResourceType {
+        BUNDLE,
+        CONFIG
+    }
+    
+    private final RegisteredResource.ResourceType resourceType;
+	
+	public static final String DIGEST_TYPE = "MD5";
+    public static final String ENTITY_JAR_PREFIX = "jar:";
+	public static final String ENTITY_BUNDLE_PREFIX = "bundle:";
+	public static final String ENTITY_CONFIG_PREFIX = "config:";
+	
+	/** Create a RegisteredResource from given data. If the data's extension
+	 *  maps to a configuration and the data provides an input stream, it is
+	 *  converted to a Dictionary 
+	 */
+	public RegisteredResourceImpl(BundleContext ctx, InstallableResource input) throws IOException {
+	    
+	    try {
+    		url = input.getUrl();
+    		resourceType = computeResourceType(input.getExtension());
+    		
+    		if(resourceType == RegisteredResource.ResourceType.BUNDLE) {
+                if(input.getInputStream() == null) {
+                    throw new IllegalArgumentException("InputStream is required for BUNDLE resource type: " + input);
+                }
+                dictionary = null;
+                dataFile = getDataFile(ctx);
+                copyToLocalStorage(input.getInputStream(), dataFile);
+                digest = input.getDigest();
+                if(digest == null || digest.length() == 0) {
+                    throw new IllegalArgumentException(
+                            "Digest must be supplied for BUNDLE resource type: " + input);
+                }
+                setAttributesFromManifest();
+                final String name = (String)attributes.get(Constants.BUNDLE_SYMBOLICNAME); 
+                if(name == null) {
+                    // not a bundle - use "jar" entity to make it easier to find out
+                    entity = ENTITY_JAR_PREFIX + input.getUrl();
+                } else {
+                    entity = ENTITY_BUNDLE_PREFIX + name;
+                }
+    		} else {
+                dataFile = null;
+                entity = ENTITY_CONFIG_PREFIX + new ConfigurationPid(input.getUrl()).getCompositePid();
+                if(input.getInputStream() == null) {
+                    // config provided as a Dictionary
+                    dictionary = copy(input.getDictionary());
+                } else {
+                    dictionary = readDictionary(input.getInputStream()); 
+                }
+                try {
+                    digest = computeDigest(dictionary);
+                } catch(NoSuchAlgorithmException nse) {
+                    throw new IOException("NoSuchAlgorithmException:" + DIGEST_TYPE);
+                }
+    		}
+    	} finally {
+    		if(input.getInputStream() != null) {
+    			input.getInputStream().close();
+    		}
+    	}
+	}
+	
+	protected File getDataFile(BundleContext ctx) throws IOException {
+		String filename = null;
+		synchronized (getClass()) {
+			filename = getClass().getSimpleName() + "." + fileNumber++;
+		}
+		return ctx.getDataFile(filename);
+	}
+	
+	public void cleanup() {
+		if(dataFile != null && dataFile.exists()) {
+			dataFile.delete();
+		}
+	}
+	
+	public String getURL() {
+		return url;
+	}
+	
+	public InputStream getInputStream() throws IOException {
+		if(dataFile == null) {
+			return null;
+		}
+		return new BufferedInputStream(new FileInputStream(dataFile));
+	}
+	
+	public Dictionary<String, Object> getDictionary() {
+		return dictionary;
+	}
+	
+	public String getDigest() {
+		return digest;
+	}
+	
+    /** Digest is needed to detect changes in data 
+     * @throws  */
+    static String computeDigest(Dictionary<String, Object> data) throws IOException, NoSuchAlgorithmException {
+        final MessageDigest d = MessageDigest.getInstance(DIGEST_TYPE);
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        final ObjectOutputStream oos = new ObjectOutputStream(bos);
+        oos.writeObject(data);
+        bos.flush();
+        d.update(bos.toByteArray());
+        return digestToString(d);
+    }
+
+    /** convert digest to readable string (http://www.javalobby.org/java/forums/t84420.html) */
+    private static String digestToString(MessageDigest d) {
+        final BigInteger bigInt = new BigInteger(1, d.digest());
+        return new String(bigInt.toString(16));
+    }
+    
+    /** Copy data to local storage */
+	private void copyToLocalStorage(InputStream data, File f) throws IOException {
+		final OutputStream os = new BufferedOutputStream(new FileOutputStream(f));
+		try {
+			final byte[] buffer = new byte[16384];
+			int count = 0;
+			while( (count = data.read(buffer, 0, buffer.length)) > 0) {
+				os.write(buffer, 0, count);
+			}
+			os.flush();
+		} finally {
+			if(os != null) {
+				os.close();
+			}
+		}
+	}
+	
+	/** Convert InputStream to Dictionary using our extended properties format,
+	 * 	which supports multi-value properties 
+	 */
+	static Dictionary<String, Object> readDictionary(InputStream is) throws IOException {
+		final Dictionary<String, Object> result = new Hashtable<String, Object>();
+		final PropertyConverter converter = new PropertyConverter();
+		final Properties p = new Properties();
+        p.load(is);
+        for(Map.Entry<Object, Object> e : p.entrySet()) {
+            final PropertyValue v = converter.convert((String)e.getKey(), (String)e.getValue());
+            result.put(v.getKey(), v.getValue());
+        }
+        return result;
+	}
+	
+	/** Copy given Dictionary, sorting keys */
+	static Dictionary<String, Object> copy(Dictionary<String, Object> d) {
+	    final Dictionary<String, Object> result = new Hashtable<String, Object>();
+	    final List<String> keys = new ArrayList<String>();
+	    final Enumeration<String> e = d.keys();
+	    while(e.hasMoreElements()) {
+	        keys.add(e.nextElement());
+	    }
+	    Collections.sort(keys);
+	    for(String key : keys) {
+	        result.put(key, d.get(key));
+	    }
+	    return result;
+	}
+	
+	public String getUrl() {
+	    return url;
+	}
+
+    public RegisteredResource.ResourceType getResourceType() {
+        return resourceType;
+    }
+    
+    static RegisteredResource.ResourceType computeResourceType(String extension) {
+        if(extension.equals("jar")) {
+            return RegisteredResource.ResourceType.BUNDLE;
+        } else {
+            return RegisteredResource.ResourceType.CONFIG;
+        }
+    }
+    
+    /** Return the identifier of the OSGi "entity" that this resource
+     *  represents, for example "bundle:SID" where SID is the bundle's
+     *  symbolic ID, or "config:PID" where PID is config's PID. 
+     */
+    public String getEntityId() {
+        return entity;
+    }
+    
+    public Map<String, Object> getAttributes() {
+		return attributes;
+	}
+    
+	public boolean isInstallable() {
+        return installable;
+	}
+
+    public void setInstallable(boolean installable) {
+        this.installable = installable;
+    }
+
+    /** Read the manifest from supplied input stream, which is closed before return */
+    static Manifest getManifest(InputStream ins) throws IOException {
+        Manifest result = null;
+
+        JarInputStream jis = null;
+        try {
+            jis = new JarInputStream(ins);
+            result= jis.getManifest();
+
+        } finally {
+
+            // close the jar stream or the inputstream, if the jar
+            // stream is set, we don't need to close the input stream
+            // since closing the jar stream closes the input stream
+            if (jis != null) {
+                try {
+                    jis.close();
+                } catch (IOException ignore) {
+                }
+            } else {
+                try {
+                    ins.close();
+                } catch (IOException ignore) {
+                }
+            }
+        }
+
+        return result;
+    }
+    
+    private void setAttributesFromManifest() throws IOException {
+    	final Manifest m = getManifest(getInputStream());
+        if(m != null) {
+            attributes.put(Constants.BUNDLE_SYMBOLICNAME, m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME));
+            attributes.put(Constants.BUNDLE_VERSION, 
+            		new Version(m.getMainAttributes().getValue(Constants.BUNDLE_VERSION)));
+        }
+    }
+}
\ No newline at end of file

Propchange: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallRemoveTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallRemoveTask.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallRemoveTask.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallRemoveTask.java Mon Aug 17 19:36:18 2009
@@ -50,7 +50,7 @@
 		if(isInstallOrUpdate()) {
 			return TaskOrder.BUNDLE_INSTALL_ORDER + uri;
 		} else {
-			return TaskOrder.BUNDLE_UNINSTALL_ORDER + uri;
+			return TaskOrder.BUNDLE_REMOVE_ORDER + uri;
 		}
 	}
 
@@ -87,17 +87,10 @@
     		throw new IOException("RegisteredResource does not adapt to an InputStream: " + uri);
     	}
 
-		final Manifest m = data.getManifest();
-		if(m == null) {
-			throw new IOException("Manifest not found for RegisteredResource " + uri);
-		}
-
         // Update if we already have a bundle id, else install
-		Bundle b;
-		boolean updated = false;
+		Bundle b = null;
 		try {
 			b = null;
-			updated = false;
 
 			// check whether we know the bundle and it exists
 			final Long longId = (Long) attributes.get(Storage.KEY_BUNDLE_ID);
@@ -108,7 +101,7 @@
 			// either we don't know the bundle yet or it does not exist,
 			// so check whether the bundle can be found by its symbolic name
 			if (b == null) {
-			    b = TaskUtilities.getMatchingBundle(bundleContext, m);
+			    b = TaskUtilities.getMatchingBundle(bundleContext, (String)data.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME));
 			}
 
 			// If the bundle (or one with the same symbolic name) is
@@ -116,7 +109,7 @@
 			// version
 			if (b != null) {
 				final Version installedVersion = new Version((String)(b.getHeaders().get(Constants.BUNDLE_VERSION)));
-				final Version newBundleVersion = new Version(m.getMainAttributes().getValue(Constants.BUNDLE_VERSION));
+				final Version newBundleVersion = (Version)(data.getAttributes().get(Constants.BUNDLE_VERSION));
 				if(ignoreNewBundle(b.getSymbolicName(), uri, installedVersion, newBundleVersion)) {
 		            return false;
 				}
@@ -130,7 +123,6 @@
 		    	}
 		        b.stop();
 		        b.update(is);
-			    updated = true;
 			    tctx.addTaskToCurrentCycle(new SynchronousRefreshPackagesTask());
 			    tctx.addTaskToCurrentCycle(new BundleStartTask(b.getBundleId()));
 			} else {

Added: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java?rev=805111&view=auto
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java (added)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java Mon Aug 17 19:36:18 2009
@@ -0,0 +1,57 @@
+/*
+ * 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.sling.osgi.installer.impl.tasks;
+
+import org.apache.sling.osgi.installer.OsgiInstaller;
+import org.apache.sling.osgi.installer.impl.OsgiInstallerContext;
+import org.apache.sling.osgi.installer.impl.OsgiInstallerTask;
+import org.apache.sling.osgi.installer.impl.RegisteredResource;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+/** Remove a bundle from a RegisteredResource. Creates
+ *  a refresh packages task when executed.
+ */
+public class BundleRemoveTask extends OsgiInstallerTask {
+
+    private final RegisteredResource resource;
+    
+    public BundleRemoveTask(RegisteredResource r) {
+        this.resource = r;
+    }
+    
+    @Override
+    public void execute(OsgiInstallerContext ctx) throws Exception {
+        final String symbolicName = (String)resource.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME);
+        final Bundle b = TaskUtilities.getMatchingBundle(ctx.getBundleContext(), symbolicName);
+        if(b == null) {
+            throw new IllegalStateException("Bundle to remove (" + symbolicName + ") not found");
+        }
+        b.stop();
+        b.uninstall();
+        ctx.addTaskToCurrentCycle(new SynchronousRefreshPackagesTask());
+        ctx.incrementCounter(OsgiInstaller.OSGI_TASKS_COUNTER);
+    }
+
+    @Override
+    public String getSortKey() {
+        return TaskOrder.BUNDLE_REMOVE_ORDER;
+    }
+
+}

Propchange: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleUpdateTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleUpdateTask.java?rev=805111&view=auto
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleUpdateTask.java (added)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleUpdateTask.java Mon Aug 17 19:36:18 2009
@@ -0,0 +1,64 @@
+/*
+ * 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.sling.osgi.installer.impl.tasks;
+
+import org.apache.sling.osgi.installer.OsgiInstaller;
+import org.apache.sling.osgi.installer.impl.OsgiInstallerContext;
+import org.apache.sling.osgi.installer.impl.OsgiInstallerTask;
+import org.apache.sling.osgi.installer.impl.RegisteredResource;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+/** Update a bundle from a RegisteredResource. Creates
+ *  a bundleStartTask to restart the bundle if it was
+ *   active before the update.
+ */
+public class BundleUpdateTask extends OsgiInstallerTask {
+
+    private final RegisteredResource resource;
+    
+    public BundleUpdateTask(RegisteredResource r) {
+        this.resource = r;
+    }
+    
+    public RegisteredResource getResource() {
+        return resource;
+    }
+    
+    @Override
+    public void execute(OsgiInstallerContext ctx) throws Exception {
+        final String symbolicName = (String)resource.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME);
+        final Bundle b = TaskUtilities.getMatchingBundle(ctx.getBundleContext(), symbolicName);
+        if(b == null) {
+            throw new IllegalStateException("Bundle to update (" + symbolicName + ") not found");
+        }
+        if(b.getState() == Bundle.ACTIVE) {
+            ctx.addTaskToCurrentCycle(new BundleStartTask(b.getBundleId()));
+        }
+        b.stop();
+        b.update(resource.getInputStream());
+        ctx.incrementCounter(OsgiInstaller.OSGI_TASKS_COUNTER);
+    }
+
+    @Override
+    public String getSortKey() {
+        return TaskOrder.BUNDLE_UPDATE_ORDER;
+    }
+
+}

Propchange: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleUpdateTask.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleUpdateTask.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/TaskOrder.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/TaskOrder.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/TaskOrder.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/TaskOrder.java Mon Aug 17 19:36:18 2009
@@ -22,8 +22,9 @@
 class TaskOrder {
 	static final String CONFIG_UNINSTALL_ORDER = "10-";
 	static final String CONFIG_INSTALL_ORDER = "20-";
-	static final String BUNDLE_UNINSTALL_ORDER = "30-";
-	static final String BUNDLE_INSTALL_ORDER = "40-";
-	static final String REFRESH_PACKAGES_ORDER = "50-";
-	static final String BUNDLE_START_ORDER = "60-";
+	static final String BUNDLE_REMOVE_ORDER = "30-";
+    static final String BUNDLE_UPDATE_ORDER = "40-";
+	static final String BUNDLE_INSTALL_ORDER = "50-";
+	static final String REFRESH_PACKAGES_ORDER = "60-";
+	static final String BUNDLE_START_ORDER = "70-";
 }

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/TaskUtilities.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/TaskUtilities.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/TaskUtilities.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/TaskUtilities.java Mon Aug 17 19:36:18 2009
@@ -1,22 +1,17 @@
 package org.apache.sling.osgi.installer.impl.tasks;
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.util.jar.JarInputStream;
-import java.util.jar.Manifest;
 
 import org.apache.sling.osgi.installer.impl.ConfigurationPid;
 import org.apache.sling.osgi.installer.impl.OsgiInstallerContext;
-import org.apache.sling.osgi.installer.impl.RegisteredResource;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 
 /** Misc.utilities for classes of this package */
-class TaskUtilities {
+public class TaskUtilities {
     /**
      * Returns a bundle with the same symbolic name as the bundle provided in
      * the installable data. If the installable data has no manifest file or the
@@ -36,14 +31,13 @@
      *         with a symbolic name.
      * @throws IOException If an error occurrs reading from the input stream.
      */
-    static Bundle getMatchingBundle(BundleContext ctx, Manifest m) throws IOException {
+    public static Bundle getMatchingBundle(BundleContext ctx, String bundleSymbolicName) {
 
-        final String symbolicName = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
-        if (symbolicName != null) {
+        if (bundleSymbolicName != null) {
 
             Bundle[] bundles = ctx.getBundles();
             for (Bundle bundle : bundles) {
-                if (symbolicName.equals(bundle.getSymbolicName())) {
+                if (bundleSymbolicName.equals(bundle.getSymbolicName())) {
                     return bundle;
                 }
             }
@@ -53,7 +47,7 @@
     }
 
     /** Get or create configuration */
-    static Configuration getConfiguration(ConfigurationPid cp, boolean createIfNeeded, OsgiInstallerContext ocs)
+    public static Configuration getConfiguration(ConfigurationPid cp, boolean createIfNeeded, OsgiInstallerContext ocs)
     throws IOException, InvalidSyntaxException
     {
     	final ConfigurationAdmin configurationAdmin = ocs.getConfigurationAdmin();

Added: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/BundleTaskCreatorTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/BundleTaskCreatorTest.java?rev=805111&view=auto
==============================================================================
--- sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/BundleTaskCreatorTest.java (added)
+++ sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/BundleTaskCreatorTest.java Mon Aug 17 19:36:18 2009
@@ -0,0 +1,199 @@
+/*
+ * 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.sling.osgi.installer.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.sling.osgi.installer.impl.tasks.BundleInstallTask;
+import org.apache.sling.osgi.installer.impl.tasks.BundleRemoveTask;
+import org.apache.sling.osgi.installer.impl.tasks.BundleUpdateTask;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+
+public class BundleTaskCreatorTest {
+	public static final String SN = "TestSymbolicName";
+	
+	private SortedSet<OsgiInstallerTask> getTasks(RegisteredResource [] resources, BundleTaskCreator btc) {
+		final SortedSet<RegisteredResource> s = OsgiInstallerThread.createRegisteredResourcesEntry();
+		for(RegisteredResource r : resources) {
+			s.add(r);
+		}
+		
+		final SortedSet<OsgiInstallerTask> tasks = new TreeSet<OsgiInstallerTask>();
+		btc.createTasks(null, s, tasks);
+		return tasks;
+	}
+	
+	@Test 
+	public void testSingleBundleNew() {
+		final RegisteredResource [] r = {
+				new MockBundleResource(SN, "1.0")
+		};
+        final MockBundleTaskCreator c = new MockBundleTaskCreator();
+		final SortedSet<OsgiInstallerTask> s = getTasks(r, c);
+		assertEquals("Expected one task", 1, s.size());
+		assertTrue("Expected a BundleInstallTask", s.first() instanceof BundleInstallTask);
+	}
+
+	@Test
+    public void testSingleBundleAlreadyInstalled() {
+        final RegisteredResource [] r = {
+                new MockBundleResource(SN, "1.0")
+        };
+        
+        {
+            final MockBundleTaskCreator c = new MockBundleTaskCreator();
+            c.addBundleInfo(SN, "1.0", Bundle.ACTIVE);
+            final SortedSet<OsgiInstallerTask> s = getTasks(r, c);
+            assertEquals("Expected no tasks, same version is active", 0, s.size());
+        }
+        
+        {
+            final MockBundleTaskCreator c = new MockBundleTaskCreator();
+            c.addBundleInfo(SN, "1.0", Bundle.RESOLVED);
+            final SortedSet<OsgiInstallerTask> s = getTasks(r, c);
+            assertEquals("Expected no tasks, same version is installed", 0, s.size());
+        }
+    }
+	
+    @Test 
+    public void testBundleUpgrade() {
+        final RegisteredResource [] r = {
+                new MockBundleResource(SN, "1.1")
+        };
+        
+        {
+            final MockBundleTaskCreator c = new MockBundleTaskCreator();
+            c.addBundleInfo(SN, "1.0", Bundle.ACTIVE);
+            final SortedSet<OsgiInstallerTask> s = getTasks(r, c);
+            assertEquals("Expected one task", 1, s.size());
+            assertTrue("Expected a BundleUpdateTask", s.first() instanceof BundleUpdateTask);
+        }
+    }
+    
+    @Test 
+    public void testBundleUpgradeBothRegistered() {
+        final RegisteredResource [] r = {
+                new MockBundleResource(SN, "1.1"),
+                new MockBundleResource(SN, "1.0")
+        };
+        
+        {
+            final MockBundleTaskCreator c = new MockBundleTaskCreator();
+            c.addBundleInfo(SN, "1.0", Bundle.ACTIVE);
+            final SortedSet<OsgiInstallerTask> s = getTasks(r, c);
+            assertEquals("Expected one task", 1, s.size());
+            assertTrue("Expected a BundleUpdateTask", s.first() instanceof BundleUpdateTask);
+        }
+    }
+    
+    @Test 
+    public void testBundleUpgradeBothRegisteredReversed() {
+        final RegisteredResource [] r = {
+                new MockBundleResource(SN, "1.0"),
+                new MockBundleResource(SN, "1.1")
+        };
+        
+        {
+            final MockBundleTaskCreator c = new MockBundleTaskCreator();
+            c.addBundleInfo(SN, "1.0", Bundle.ACTIVE);
+            final SortedSet<OsgiInstallerTask> s = getTasks(r, c);
+            assertEquals("Expected one task", 1, s.size());
+            assertTrue("Expected a BundleUpdateTask", s.first() instanceof BundleUpdateTask);
+        }
+    }
+    
+    @Test 
+    public void testBundleUpgradeSnapshot() {
+        final String v = "2.0.7.-SNAPSHOT";
+        final RegisteredResource [] r = {
+                new MockBundleResource(SN, v)
+        };
+        
+        {
+            final MockBundleTaskCreator c = new MockBundleTaskCreator();
+            c.addBundleInfo(SN, v, Bundle.ACTIVE);
+            final SortedSet<OsgiInstallerTask> s = getTasks(r, c);
+            assertEquals("Expected one task", 1, s.size());
+            assertTrue("Expected a BundleUpdateTask", s.first() instanceof BundleUpdateTask);
+        }
+    }
+    
+    @Test 
+    public void testBundleRemoveSingle() {
+        final RegisteredResource [] r = {
+                new MockBundleResource(SN, "1.0")
+        };
+        r[0].setInstallable(false);
+        
+        {
+            final MockBundleTaskCreator c = new MockBundleTaskCreator();
+            c.addBundleInfo(SN, "1.0", Bundle.ACTIVE);
+            final SortedSet<OsgiInstallerTask> s = getTasks(r, c);
+            assertEquals("Expected one task", 1, s.size());
+            assertTrue("Expected a BundleRemoveTask", s.first() instanceof BundleRemoveTask);
+        }
+    }
+    
+    @Test 
+    public void testBundleRemoveMultiple() {
+        final RegisteredResource [] r = {
+                new MockBundleResource(SN, "1.0"),
+                new MockBundleResource(SN, "1.1"),
+                new MockBundleResource(SN, "2.0")
+        };
+        for(RegisteredResource x : r) {
+            x.setInstallable(false);
+        }
+        
+        {
+            final MockBundleTaskCreator c = new MockBundleTaskCreator();
+            c.addBundleInfo(SN, "1.1", Bundle.ACTIVE);
+            final SortedSet<OsgiInstallerTask> s = getTasks(r, c);
+            assertEquals("Expected one task", 1, s.size());
+            assertTrue("Expected a BundleRemoveTask", s.first() instanceof BundleRemoveTask);
+        }
+    }
+    
+    @Test 
+    public void testDowngradeOfRemovedResource() {
+        final RegisteredResource [] r = {
+                new MockBundleResource(SN, "1.0"),
+                new MockBundleResource(SN, "1.1"),
+        };
+        
+        // Simulate V1.1 installed but resource is gone -> downgrade to 1.0
+        r[1].setInstallable(false); 
+        
+       {
+            final MockBundleTaskCreator c = new MockBundleTaskCreator();
+            c.addBundleInfo(SN, "1.1", Bundle.ACTIVE);
+            final SortedSet<OsgiInstallerTask> s = getTasks(r, c);
+            assertEquals("Expected one task", 1, s.size());
+            assertTrue("Expected a BundleUpdateTask", s.first() instanceof BundleUpdateTask);
+            final BundleUpdateTask t = (BundleUpdateTask)s.first();
+            assertEquals("Update should be to V1.0", r[0], t.getResource());
+        }
+    }
+    
+}
\ No newline at end of file

Propchange: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/BundleTaskCreatorTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/BundleTaskCreatorTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/DictionaryConversionTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/DictionaryConversionTest.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/DictionaryConversionTest.java (original)
+++ sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/DictionaryConversionTest.java Mon Aug 17 19:36:18 2009
@@ -74,7 +74,7 @@
             ;
         
         final ByteArrayInputStream is = new ByteArrayInputStream(data.getBytes());
-        final Dictionary<?, ?> d = RegisteredResource.readDictionary(is);
+        final Dictionary<?, ?> d = RegisteredResourceImpl.readDictionary(is);
         is.close();
         
         assertEquals("Number of entries must match", 4, d.size());
@@ -96,7 +96,7 @@
             ;
         
         final ByteArrayInputStream is = new ByteArrayInputStream(data.getBytes());
-        final Dictionary<?, ?> d = RegisteredResource.readDictionary(is);
+        final Dictionary<?, ?> d = RegisteredResourceImpl.readDictionary(is);
         is.close();
         
         assertEquals("Number of entries must match", 5, d.size());

Modified: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/DictionaryDigestTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/DictionaryDigestTest.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/DictionaryDigestTest.java (original)
+++ sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/DictionaryDigestTest.java Mon Aug 17 19:36:18 2009
@@ -33,7 +33,7 @@
 	
 	private String testDigestChanged(Dictionary<String, Object> d, 
 			String oldDigest, int step, boolean shouldChange) throws Exception {
-		final String newDigest = RegisteredResource.computeDigest(d);
+		final String newDigest = RegisteredResourceImpl.computeDigest(d);
 		if(shouldChange) {
 			assertTrue("Digest (" + newDigest + ") should have changed at step " + step, !newDigest.equals(oldDigest));
 		} else {
@@ -51,8 +51,8 @@
 		
 		assertEquals(
 				"Two dictionary with same values have the same key", 
-				RegisteredResource.computeDigest(d1),
-				RegisteredResource.computeDigest(d2)
+				RegisteredResourceImpl.computeDigest(d1),
+				RegisteredResourceImpl.computeDigest(d2)
 		);
 	}
 	

Modified: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/LocalFileRegisteredResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/LocalFileRegisteredResource.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/LocalFileRegisteredResource.java (original)
+++ sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/LocalFileRegisteredResource.java Mon Aug 17 19:36:18 2009
@@ -25,7 +25,7 @@
 import org.osgi.framework.BundleContext;
 
 /** RegisteredResource that stores data to a local temporary file */
-class LocalFileRegisteredResource extends RegisteredResource {
+class LocalFileRegisteredResource extends RegisteredResourceImpl {
 	private File storage;
 	
 	LocalFileRegisteredResource(InstallableResource r) throws IOException {

Added: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleResource.java?rev=805111&view=auto
==============================================================================
--- sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleResource.java (added)
+++ sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleResource.java Mon Aug 17 19:36:18 2009
@@ -0,0 +1,83 @@
+/*
+ * 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.sling.osgi.installer.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+/** Mock RegisteredResource that simulates a bundle */
+public class MockBundleResource implements RegisteredResource {
+
+	private final Map<String, Object> attributes = new HashMap<String, Object>();
+	private boolean installable = true;
+	
+	MockBundleResource(String symbolicName, String version) {
+		attributes.put(Constants.BUNDLE_SYMBOLICNAME, symbolicName);
+		attributes.put(Constants.BUNDLE_VERSION, new Version(version));
+	}
+	
+	public void cleanup() {
+	}
+
+	public Map<String, Object> getAttributes() {
+		return attributes;
+	}
+
+	public Dictionary<String, Object> getDictionary() {
+		return null;
+	}
+
+	public String getDigest() {
+		return null;
+	}
+
+	public String getEntityId() {
+		return null;
+	}
+
+	public InputStream getInputStream() throws IOException {
+		return null;
+	}
+
+	public ResourceType getResourceType() {
+		return RegisteredResource.ResourceType.BUNDLE;
+	}
+
+	public String getUrl() {
+		return null;
+	}
+
+	public String getURL() {
+		return null;
+	}
+
+    public boolean isInstallable() {
+        return installable;
+    }
+
+    public void setInstallable(boolean installable) {
+        this.installable = installable;
+    }
+}

Propchange: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleResource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleResource.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleTaskCreator.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleTaskCreator.java?rev=805111&view=auto
==============================================================================
--- sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleTaskCreator.java (added)
+++ sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleTaskCreator.java Mon Aug 17 19:36:18 2009
@@ -0,0 +1,42 @@
+/*
+ * 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.sling.osgi.installer.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+/** BundleTaskCreator that simulates the presence and state of bundles */
+class MockBundleTaskCreator extends BundleTaskCreator {
+
+    private final Map<String, BundleInfo> fakeBundleInfo = new HashMap<String, BundleInfo>();
+    
+    void addBundleInfo(String symbolicName, String version, int state) {
+        fakeBundleInfo.put(symbolicName, new BundleInfo(new Version(version), state));
+    }
+    
+    @Override
+    protected BundleInfo getBundleInfo(BundleContext ctx, RegisteredResource bundle) {
+        return fakeBundleInfo.get(bundle.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME));
+    }
+    
+}

Propchange: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleTaskCreator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleTaskCreator.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceTest.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceTest.java (original)
+++ sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceTest.java Mon Aug 17 19:36:18 2009
@@ -34,6 +34,7 @@
 import java.util.Hashtable;
 
 import org.apache.sling.osgi.installer.InstallableResource;
+import org.osgi.framework.Constants;
 
 public class RegisteredResourceTest {
 	
@@ -144,8 +145,8 @@
             d2.put(keys[i], keys[i] + "." + keys[i]);
         }
         
-        final RegisteredResource r1 = new RegisteredResource(null, new InstallableResource("url1", d1));
-        final RegisteredResource r2 = new RegisteredResource(null, new InstallableResource("url1", d2));
+        final RegisteredResource r1 = new RegisteredResourceImpl(null, new InstallableResource("url1", d1));
+        final RegisteredResource r2 = new RegisteredResourceImpl(null, new InstallableResource("url1", d2));
         
         assertEquals(
                 "Two RegisteredResource (Dictionary) with same values but different key orderings must have the same key", 
@@ -161,8 +162,8 @@
         final ByteArrayInputStream s1 = new ByteArrayInputStream(d1.getBytes());
         final ByteArrayInputStream s2 = new ByteArrayInputStream(d2.getBytes());
         
-        final RegisteredResource r1 = new RegisteredResource(null, new InstallableResource("url1.properties", s1, null));
-        final RegisteredResource r2 = new RegisteredResource(null, new InstallableResource("url2.properties", s2, null));
+        final RegisteredResource r1 = new RegisteredResourceImpl(null, new InstallableResource("url1.properties", s1, null));
+        final RegisteredResource r2 = new RegisteredResourceImpl(null, new InstallableResource("url2.properties", s2, null));
         
         assertEquals(
                 "Two RegisteredResource (InputStream) with same values but different key orderings must have the same key", 
@@ -175,14 +176,14 @@
         final File f = getTestBundle("testbundle-1.0.jar");
         final InstallableResource i = new InstallableResource(f.getAbsolutePath(), new FileInputStream(f), f.getName());
         final RegisteredResource r = new LocalFileRegisteredResource(i);
-        assertNotNull("RegisteredResource must have manifest", r.getManifest());
+        assertNotNull("RegisteredResource must have bundle symbolic name", r.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME));
         assertEquals("RegisteredResource entity ID must match", "bundle:osgi-installer-testbundle", r.getEntityId());
     }
     
     @org.junit.Test public void testConfigEntity() throws Exception {
         final InstallableResource i = new InstallableResource("/foo/someconfig", new Hashtable<String, Object>());
         final RegisteredResource r = new LocalFileRegisteredResource(i);
-        assertNull("RegisteredResource must not have manifest", r.getManifest());
+        assertNull("RegisteredResource must not have bundle symbolic name", r.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME));
         assertEquals("RegisteredResource entity ID must match", "config:someconfig", r.getEntityId());
     }
 }
\ No newline at end of file

Modified: sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/TaskOrderingTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/TaskOrderingTest.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/TaskOrderingTest.java (original)
+++ sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/TaskOrderingTest.java Mon Aug 17 19:36:18 2009
@@ -35,6 +35,7 @@
 /** Test the ordering and duplicates elimination of
  * 	OsgiControllerTasks
  */
+// TODO add bundleUpdateTask and others
 public class TaskOrderingTest {
 
 	private Set<OsgiInstallerTask> taskSet;
@@ -46,7 +47,7 @@
 	}
 	
 	private static RegisteredResource getRegisteredResource(String url) throws IOException {
-		return new RegisteredResource(null, new InstallableResource(url, new Hashtable<String, Object>()));
+		return new RegisteredResourceImpl(null, new InstallableResource(url, new Hashtable<String, Object>()));
 	}
 	
 	private void assertOrder(int testId, Collection<OsgiInstallerTask> actual, OsgiInstallerTask [] expected) {

Modified: sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/BundleInstallTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/BundleInstallTest.java?rev=805111&r1=805110&r2=805111&view=diff
==============================================================================
--- sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/BundleInstallTest.java (original)
+++ sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/BundleInstallTest.java Mon Aug 17 19:36:18 2009
@@ -71,14 +71,13 @@
         	assertEquals("Version must be 1.1", "1.1", b.getHeaders().get(BUNDLE_VERSION));
     	}
 
-        /** TODO
     	// Upgrade to later version, verify
     	{
     	    resetCounters();
             installer.addResource(getInstallableResource(
                     getTestBundle("org.apache.sling.osgi.installer.it-" + POM_VERSION + "-testbundle-1.2.jar")));
             // wait for two tasks: update (includes stop) and start
-            waitForInstallerAction(OsgiInstaller.OSGI_TASKS_COUNTER, 1);
+            waitForInstallerAction(OsgiInstaller.OSGI_TASKS_COUNTER, 2);
         	final Bundle b = findBundle(symbolicName);
         	assertNotNull("Test bundle 1.2 must be found after waitForInstallerAction", b);
         	assertEquals("Installed bundle must be started", Bundle.ACTIVE, b.getState());
@@ -87,16 +86,21 @@
     	}
 
     	// Downgrade to lower version, installed bundle must not change
-    	{
-        	c.scheduleInstallOrUpdate(uri, new FileInstallableResource(getTestBundle("org.apache.sling.osgi.installer.it-" + POM_VERSION + "-testbundle-1.0.jar")));
-        	c.waitForInstallerAction();
-        	final Bundle b = findBundle(symbolicName);
-        	assertNotNull("Test bundle 1.2 must be found after waitForInstallerAction", b);
-        	assertEquals("Installed bundle must be started", Bundle.ACTIVE, b.getState());
-        	assertEquals("Version must be 1.2 after ignored downgrade", "1.2", b.getHeaders().get(BUNDLE_VERSION));
-        	assertEquals("Bundle ID must not change after downgrade", bundleId, b.getBundleId());
-    	}
+        {
+            resetCounters();
+            installer.addResource(getInstallableResource(
+                    getTestBundle("org.apache.sling.osgi.installer.it-" + POM_VERSION + "-testbundle-1.0.jar")));
+            
+            // wait for two cycles to make sure no updates happen
+            waitForInstallerAction(OsgiInstaller.INSTALLER_CYCLES_COUNTER, 2);
+            final Bundle b = findBundle(symbolicName);
+            assertNotNull("Test bundle 1.2 must still be found after waitForInstallerAction", b);
+            assertEquals("Installed bundle must be started", Bundle.ACTIVE, b.getState());
+            assertEquals("Version must be 1.2 after ignored downgrade", "1.2", b.getHeaders().get(BUNDLE_VERSION));
+            assertEquals("Bundle ID must not change after ignored downgrade", bundleId, b.getBundleId());
+        }
     	
+        /** TODO
     	// Uninstall
     	{
         	c.scheduleUninstall(uri);