[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [equinox-dev] new delta algorithm and tests


I did a mess of refactoring to converge the jbdiff, jardelta and pack optimizers and then started on the tests (I've not looked much at the processors).   Unfortunately, I have some problem with the JBdiff zip tests (both delta and patch).  Seems the lengths are not matching and I cannot figure out why.  Perhaps I messed something up.  I also have trouble with the pack200 tests but that one I believe to be due to bad data.  I'm going to refactor the test to be more like what you did for the JBdiff tests (mock repos etc) as that looks way easier to manage the data.  I'm hesitant to release the changes as they break the tests.  Bugzilla is not working for me right now so I can't create a bug report and attach a patch.  So here is a patch with what I have so far.  Take a look if you have time and see what you think.  In particular, if you can see where I've gone wrong with the jbdiff stuff that would be appreciated.

Jeff




Stefan Liebig <Stefan.Liebig@xxxxxxxxxxxx>
Sent by: equinox-dev-bounces@xxxxxxxxxxx

11/10/2007 10:31 AM

Please respond to
Equinox development mailing list <equinox-dev@xxxxxxxxxxx>

To
Equinox development mailing list <equinox-dev@xxxxxxxxxxx>
cc
Subject
Re: [equinox-dev] new delta algorithm and tests





Yes, I can have a look at ´optimizing´ what we now have now. Which day did you mean with ´tomorrow´? If have already a few starting points, let´share them.
However, I am fighting currently with
https://bugs.eclipse.org/bugs/show_bug.cgi?id=209233. I would like to have that solved before I start refactoring.

Stefan


Jeff McAffer wrote:


I integrated a simplistic JAR delta optimizer and processor with Stefan's JBdiff stuff and added some tests.  More is needed in the area but this delta is based on comparisons of JAR entries.  The delta is encoded as a JAR containing the changes from the base.   Whole entries that changed are included in the delta.  This is very simple / efficient to compute but will likely produce larger deltas than jbdiff.  Note that since the delta is just  JAR of class files, it too can be packed! ;-)  Anyway, I also enhanced the tests.


One thing that remains is to unify the code.  Turns out that the pack200, jardelta and jbdiff optimizers, processors and tests have a ton of duplicated code.  Much of it is fussy stuff like making sure that streams are closed etc.  This should all be shared.  Stefan, you might want to take a stab at this?  Or I can look at it tomorrow...


Jeff




_______________________________________________
equinox-dev mailing list
equinox-dev@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/equinox-dev
 
_______________________________________________
equinox-dev mailing list
equinox-dev@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/equinox-dev

### Eclipse Workspace Patch 1.0
#P org.eclipse.equinox.p2.tests
Index: src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/TestArtifactKey.java
===================================================================
RCS file: src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/TestArtifactKey.java
diff -N src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/TestArtifactKey.java
--- src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/TestArtifactKey.java	8 Nov 2007 13:31:56 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,47 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007 compeople AG and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * 	compeople AG (Stefan Liebig) - initial API and implementation
- *******************************************************************************/
-package org.eclipse.equinox.p2.tests.artifact.processor.jbdiff;
-
-import org.eclipse.equinox.p2.metadata.IArtifactKey;
-import org.osgi.framework.Version;
-
-public class TestArtifactKey implements IArtifactKey {
-
-	private String namespace;
-	private String classifier;
-	private String id;
-	private Version version;
-
-	public TestArtifactKey(String namespace, String classifier, String id, Version version) {
-		super();
-		this.namespace = namespace;
-		this.classifier = classifier;
-		this.id = id;
-		this.version = version;
-	}
-
-	public String getClassifier() {
-		return classifier;
-	}
-
-	public String getId() {
-		return id;
-	}
-
-	public String getNamespace() {
-		return namespace;
-	}
-
-	public Version getVersion() {
-		return version;
-	}
-
-}
Index: src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/JBPatchZipStepTest.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/JBPatchZipStepTest.java,v
retrieving revision 1.2
diff -u -r1.2 JBPatchZipStepTest.java
--- src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/JBPatchZipStepTest.java	8 Nov 2007 18:23:36 -0000	1.2
+++ src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/JBPatchZipStepTest.java	12 Nov 2007 02:14:46 -0000
@@ -14,6 +14,7 @@
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.equinox.internal.p2.artifact.processors.jbdiff.JBPatchZipStep;
 import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
 import org.eclipse.equinox.p2.artifact.repository.ArtifactDescriptor;
 import org.eclipse.equinox.p2.artifact.repository.IArtifactRepository;
 import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStep;
@@ -60,7 +61,7 @@
 		IArtifactRepository repoMock = ArtifactRepositoryMock.getMock("testData/delta/org.eclipse.jdt_3.2.0.v20060605-1400.njar");
 		ProcessingStep patcher = new MockableJBPatchZipStep(repoMock);
 		ProcessingStepDescriptor descriptor = new ProcessingStepDescriptor("id", "ns,cl,id1,1.0", true);
-		IArtifactKey key = new TestArtifactKey("ns", "cl", "id1", new Version("1.1"));
+		IArtifactKey key = new ArtifactKey("ns", "cl", "id1", new Version("1.1"));
 		ArtifactDescriptor context = new ArtifactDescriptor(key);
 		patcher.initialize(descriptor, context);
 
Index: src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/ArtifactkeyDeSerializerTest.java
===================================================================
RCS file: src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/ArtifactkeyDeSerializerTest.java
diff -N src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/ArtifactkeyDeSerializerTest.java
--- src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/ArtifactkeyDeSerializerTest.java	8 Nov 2007 13:31:56 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,113 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007 compeople AG and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * 	compeople AG (Stefan Liebig) - initial API and implementation
- *******************************************************************************/
-package org.eclipse.equinox.p2.tests.artifact.processor.jbdiff;
-
-import junit.framework.TestCase;
-import org.eclipse.equinox.internal.p2.artifact.processors.jbdiff.ArtifactKeyDeSerializer;
-import org.eclipse.equinox.p2.metadata.IArtifactKey;
-import org.osgi.framework.Version;
-
-/**
- * Test <code>ArtifactkeyDeSerializer</code>
- */
-public class ArtifactkeyDeSerializerTest extends TestCase {
-
-	public void testSerialize() {
-		IArtifactKey key = new TestArtifactKey("namespace", "classifier", "identifier", new Version("1.0"));
-		assertEquals("namespace,classifier,identifier,1.0.0", ArtifactKeyDeSerializer.serialize(key));
-	}
-
-	public void testSerializeEmptyNamespace() {
-		IArtifactKey key = new TestArtifactKey("", "classifier", "identifier", new Version("1.0"));
-		assertEquals(",classifier,identifier,1.0.0", ArtifactKeyDeSerializer.serialize(key));
-	}
-
-	public void testDeserialize() {
-		IArtifactKey key = ArtifactKeyDeSerializer.deserialize("namespace,classifier,identifier,1.0.0");
-		assertNotNull(key);
-		assertEquals("namespace", key.getNamespace());
-		assertEquals("classifier", key.getClassifier());
-		assertEquals("identifier", key.getId());
-		assertEquals(new Version("1.0"), key.getVersion());
-	}
-
-	public void testDeserializeEmptyNamespace() {
-		IArtifactKey key = ArtifactKeyDeSerializer.deserialize(",classifier,identifier,1.0.0");
-		assertNotNull(key);
-		assertEquals("", key.getNamespace());
-		assertEquals("classifier", key.getClassifier());
-		assertEquals("identifier", key.getId());
-		assertEquals(new Version("1.0"), key.getVersion());
-	}
-
-	public void testDeserializeEmptyClassifier() {
-		IArtifactKey key = ArtifactKeyDeSerializer.deserialize("namespace,,identifier,1.0.0");
-		assertNotNull(key);
-		assertEquals("namespace", key.getNamespace());
-		assertEquals("", key.getClassifier());
-		assertEquals("identifier", key.getId());
-		assertEquals(new Version("1.0"), key.getVersion());
-	}
-
-	public void testDeserializeEmptyIdentifier() {
-		IArtifactKey key = ArtifactKeyDeSerializer.deserialize("namespace,classifier,,1.0.0");
-		assertNotNull(key);
-		assertEquals("namespace", key.getNamespace());
-		assertEquals("classifier", key.getClassifier());
-		assertEquals("", key.getId());
-		assertEquals(new Version("1.0"), key.getVersion());
-	}
-
-	public void testDeserializeEmptyVersion() {
-		IArtifactKey key = ArtifactKeyDeSerializer.deserialize("namespace,classifier,identifier,");
-		assertNotNull(key);
-		assertEquals("namespace", key.getNamespace());
-		assertEquals("classifier", key.getClassifier());
-		assertEquals("identifier", key.getId());
-		assertEquals(new Version("0.0"), key.getVersion());
-	}
-
-	public void testDeserializeEmptyEverything() {
-		IArtifactKey key = ArtifactKeyDeSerializer.deserialize(",,,");
-		assertNotNull(key);
-		assertEquals("", key.getNamespace());
-		assertEquals("", key.getClassifier());
-		assertEquals("", key.getId());
-		assertEquals(new Version("0.0"), key.getVersion());
-	}
-
-	public void testDeserializeTooFewPartsI() {
-		try {
-			ArtifactKeyDeSerializer.deserialize(",,");
-			fail();
-		} catch (IllegalArgumentException e) {
-			assertTrue(true);
-		}
-	}
-
-	public void testDeserializeTooMuchPartsI() {
-		try {
-			ArtifactKeyDeSerializer.deserialize(",,,,");
-			fail();
-		} catch (IllegalArgumentException e) {
-			assertTrue(true);
-		}
-	}
-
-	public void testDeserializeTooFewPartsII() {
-		try {
-			ArtifactKeyDeSerializer.deserialize("namespace,classifier,1.0.0");
-			fail();
-		} catch (IllegalArgumentException e) {
-			assertTrue(true);
-		}
-	}
-}
Index: src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/AllTests.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/AllTests.java,v
retrieving revision 1.1
diff -u -r1.1 AllTests.java
--- src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/AllTests.java	8 Nov 2007 13:31:56 -0000	1.1
+++ src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/AllTests.java	12 Nov 2007 02:14:46 -0000
@@ -19,7 +19,6 @@
 
 	public static Test suite() {
 		TestSuite suite = new TestSuite(AllTests.class.getName());
-		suite.addTestSuite(ArtifactkeyDeSerializerTest.class);
 		suite.addTestSuite(JBPatchStepTest.class);
 		suite.addTestSuite(JBPatchZipStepTest.class);
 		return suite;
Index: src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/JBPatchStepTest.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/JBPatchStepTest.java,v
retrieving revision 1.1
diff -u -r1.1 JBPatchStepTest.java
--- src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/JBPatchStepTest.java	8 Nov 2007 13:31:56 -0000	1.1
+++ src/org/eclipse/equinox/p2/tests/artifact/processor/jbdiff/JBPatchStepTest.java	12 Nov 2007 02:14:46 -0000
@@ -16,6 +16,7 @@
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.equinox.internal.p2.artifact.processors.jbdiff.JBPatchStep;
 import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
 import org.eclipse.equinox.p2.artifact.repository.ArtifactDescriptor;
 import org.eclipse.equinox.p2.artifact.repository.IArtifactRepository;
 import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStep;
@@ -35,7 +36,7 @@
 		IArtifactRepository repoMock = ArtifactRepositoryMock.getMock("testData/delta/eclipse-3.2.exe");
 		ProcessingStep patcher = new MockableJBPatchStep(repoMock);
 		ProcessingStepDescriptor descriptor = new ProcessingStepDescriptor("id", "ns,cl,id1,1.0", true);
-		IArtifactKey key = new TestArtifactKey("ns", "cl", "id1", new Version("1.1"));
+		IArtifactKey key = new ArtifactKey("ns", "cl", "id1", new Version("1.1"));
 		ArtifactDescriptor context = new ArtifactDescriptor(key);
 		patcher.initialize(descriptor, context);
 
Index: src/org/eclipse/equinox/p2/tests/artifact/optimizers/pack200/JarDeltaRepositoryTest.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/optimizers/pack200/JarDeltaRepositoryTest.java,v
retrieving revision 1.1
diff -u -r1.1 JarDeltaRepositoryTest.java
--- src/org/eclipse/equinox/p2/tests/artifact/optimizers/pack200/JarDeltaRepositoryTest.java	9 Nov 2007 04:22:59 -0000	1.1
+++ src/org/eclipse/equinox/p2/tests/artifact/optimizers/pack200/JarDeltaRepositoryTest.java	12 Nov 2007 02:14:46 -0000
@@ -48,7 +48,7 @@
 			FileUtils.deleteAll(workDir);
 	}
 
-	public void testJarURLRepository() {
+	public void testJarRoundTrip() {
 		URL repositoryJar = TestActivator.getContext().getBundle().getEntry("/testData/enginerepo.zip");
 		URL repositoryURL = extractRepositoryJAR(repositoryJar);
 		assertNotNull("Could not extract repository", repositoryURL);
Index: src/org/eclipse/equinox/p2/tests/artifact/optimizers/jbdiff/JBDiffStepTest.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/optimizers/jbdiff/JBDiffStepTest.java,v
retrieving revision 1.1
diff -u -r1.1 JBDiffStepTest.java
--- src/org/eclipse/equinox/p2/tests/artifact/optimizers/jbdiff/JBDiffStepTest.java	8 Nov 2007 13:31:27 -0000	1.1
+++ src/org/eclipse/equinox/p2/tests/artifact/optimizers/jbdiff/JBDiffStepTest.java	12 Nov 2007 02:14:46 -0000
@@ -16,13 +16,13 @@
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff.JBDiffStep;
 import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
 import org.eclipse.equinox.p2.artifact.repository.ArtifactDescriptor;
 import org.eclipse.equinox.p2.artifact.repository.IArtifactRepository;
 import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStepDescriptor;
 import org.eclipse.equinox.p2.metadata.IArtifactKey;
 import org.eclipse.equinox.p2.tests.TestActivator;
 import org.eclipse.equinox.p2.tests.artifact.processor.jbdiff.ArtifactRepositoryMock;
-import org.eclipse.equinox.p2.tests.artifact.processor.jbdiff.TestArtifactKey;
 import org.osgi.framework.Version;
 
 /**
@@ -40,7 +40,7 @@
 		IArtifactRepository repoMock = ArtifactRepositoryMock.getMock("testData/delta/eclipse-3.2.exe");
 		MockableJBDiffStep differ = new MockableJBDiffStep(repoMock);
 		ProcessingStepDescriptor stepDescriptor = new ProcessingStepDescriptor("id", "ns,cl,id1,1.0", true);
-		IArtifactKey key = new TestArtifactKey("ns", "cl", "id1", new Version("1.1"));
+		IArtifactKey key = new ArtifactKey("ns", "cl", "id1", new Version("1.1"));
 		ArtifactDescriptor descriptor = new ArtifactDescriptor(key);
 		differ.initialize(stepDescriptor, descriptor);
 
Index: src/org/eclipse/equinox/p2/tests/artifact/optimizers/jbdiff/JBDiffZipStepTest.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/optimizers/jbdiff/JBDiffZipStepTest.java,v
retrieving revision 1.2
diff -u -r1.2 JBDiffZipStepTest.java
--- src/org/eclipse/equinox/p2/tests/artifact/optimizers/jbdiff/JBDiffZipStepTest.java	8 Nov 2007 18:23:36 -0000	1.2
+++ src/org/eclipse/equinox/p2/tests/artifact/optimizers/jbdiff/JBDiffZipStepTest.java	12 Nov 2007 02:14:46 -0000
@@ -14,6 +14,7 @@
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff.JBDiffZipStep;
 import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
 import org.eclipse.equinox.p2.artifact.repository.ArtifactDescriptor;
 import org.eclipse.equinox.p2.artifact.repository.IArtifactRepository;
 import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStepDescriptor;
@@ -21,7 +22,6 @@
 import org.eclipse.equinox.p2.tests.AbstractProvisioningTest;
 import org.eclipse.equinox.p2.tests.TestActivator;
 import org.eclipse.equinox.p2.tests.artifact.processor.jbdiff.ArtifactRepositoryMock;
-import org.eclipse.equinox.p2.tests.artifact.processor.jbdiff.TestArtifactKey;
 import org.osgi.framework.Version;
 
 /**
@@ -39,7 +39,7 @@
 		IArtifactRepository repoMock = ArtifactRepositoryMock.getMock("testData/delta/org.eclipse.jdt_3.2.0.v20060605-1400.njar");
 		MockableJBDiffZipStep differ = new MockableJBDiffZipStep(repoMock);
 		ProcessingStepDescriptor stepDescriptor = new ProcessingStepDescriptor("id", "ns,cl,id1,1.0", true);
-		IArtifactKey key = new TestArtifactKey("ns", "cl", "id1", new Version("1.1"));
+		IArtifactKey key = new ArtifactKey("ns", "cl", "id1", new Version("1.1"));
 		ArtifactDescriptor descriptor = new ArtifactDescriptor(key);
 		differ.initialize(stepDescriptor, descriptor);
 
Index: META-INF/MANIFEST.MF
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF,v
retrieving revision 1.12
diff -u -r1.12 MANIFEST.MF
--- META-INF/MANIFEST.MF	9 Nov 2007 04:22:59 -0000	1.12
+++ META-INF/MANIFEST.MF	12 Nov 2007 02:14:46 -0000
@@ -7,6 +7,7 @@
 Bundle-Version: 0.1.0.qualifier
 Import-Package: ie.wombat.jbdiff,
  junit.framework;version="3.8.2",
+ org.eclipse.equinox.internal.p2.artifact.optimizers,
  org.eclipse.equinox.internal.p2.artifact.optimizers.jardelta,
  org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff,
  org.eclipse.equinox.internal.p2.artifact.optimizers.pack200,
Index: src/org/eclipse/equinox/p2/tests/metadata/AllTests.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/metadata/AllTests.java,v
retrieving revision 1.1
diff -u -r1.1 AllTests.java
--- src/org/eclipse/equinox/p2/tests/metadata/AllTests.java	30 Sep 2007 18:55:02 -0000	1.1
+++ src/org/eclipse/equinox/p2/tests/metadata/AllTests.java	12 Nov 2007 02:14:46 -0000
@@ -19,6 +19,7 @@
 
 	public static Test suite() {
 		TestSuite suite = new TestSuite(AllTests.class.getName());
+		suite.addTestSuite(ArtifactKeyParsingTest.class);
 		suite.addTestSuite(FragmentMethodTest.class);
 		suite.addTestSuite(FragmentTest.class);
 		suite.addTestSuite(MultipleIUAndFragmentTest.class);
Index: src/org/eclipse/equinox/p2/tests/metadata/ArtifactKeyParsingTest.java
===================================================================
RCS file: src/org/eclipse/equinox/p2/tests/metadata/ArtifactKeyParsingTest.java
diff -N src/org/eclipse/equinox/p2/tests/metadata/ArtifactKeyParsingTest.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/equinox/p2/tests/metadata/ArtifactKeyParsingTest.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2007 compeople AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * 	compeople AG (Stefan Liebig) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.tests.metadata;
+
+import junit.framework.TestCase;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
+import org.eclipse.equinox.p2.metadata.IArtifactKey;
+import org.osgi.framework.Version;
+
+/**
+ * Test <code>ArtifactkeyDeSerializer</code>
+ */
+public class ArtifactKeyParsingTest extends TestCase {
+
+	public void testSerialize() {
+		IArtifactKey key = new ArtifactKey("namespace", "classifier", "identifier", new Version("1.0"));
+		assertEquals("namespace,classifier,identifier,1.0.0", key.toExternalForm());
+	}
+
+	public void testSerializeEmptyNamespace() {
+		IArtifactKey key = new ArtifactKey("", "classifier", "identifier", new Version("1.0"));
+		assertEquals(",classifier,identifier,1.0.0", key.toExternalForm());
+	}
+
+	public void testDeserialize() {
+		IArtifactKey key = ArtifactKey.parse("namespace,classifier,identifier,1.0.0");
+		assertNotNull(key);
+		assertEquals("namespace", key.getNamespace());
+		assertEquals("classifier", key.getClassifier());
+		assertEquals("identifier", key.getId());
+		assertEquals(new Version("1.0"), key.getVersion());
+	}
+
+	public void testDeserializeEmptyNamespace() {
+		IArtifactKey key = ArtifactKey.parse(",classifier,identifier,1.0.0");
+		assertNotNull(key);
+		assertEquals("", key.getNamespace());
+		assertEquals("classifier", key.getClassifier());
+		assertEquals("identifier", key.getId());
+		assertEquals(new Version("1.0"), key.getVersion());
+	}
+
+	public void testDeserializeEmptyClassifier() {
+		IArtifactKey key = ArtifactKey.parse("namespace,,identifier,1.0.0");
+		assertNotNull(key);
+		assertEquals("namespace", key.getNamespace());
+		assertEquals("", key.getClassifier());
+		assertEquals("identifier", key.getId());
+		assertEquals(new Version("1.0"), key.getVersion());
+	}
+
+	public void testDeserializeEmptyIdentifier() {
+		IArtifactKey key = ArtifactKey.parse("namespace,classifier,,1.0.0");
+		assertNotNull(key);
+		assertEquals("namespace", key.getNamespace());
+		assertEquals("classifier", key.getClassifier());
+		assertEquals("", key.getId());
+		assertEquals(new Version("1.0"), key.getVersion());
+	}
+
+	public void testDeserializeEmptyVersion() {
+		IArtifactKey key = ArtifactKey.parse("namespace,classifier,identifier,");
+		assertNotNull(key);
+		assertEquals("namespace", key.getNamespace());
+		assertEquals("classifier", key.getClassifier());
+		assertEquals("identifier", key.getId());
+		assertEquals(new Version("0.0"), key.getVersion());
+	}
+
+	public void testDeserializeEmptyEverything() {
+		IArtifactKey key = ArtifactKey.parse(",,,");
+		assertNotNull(key);
+		assertEquals("", key.getNamespace());
+		assertEquals("", key.getClassifier());
+		assertEquals("", key.getId());
+		assertEquals(new Version("0.0"), key.getVersion());
+	}
+
+	public void testDeserializeTooFewPartsI() {
+		try {
+			ArtifactKey.parse(",");
+			fail();
+		} catch (IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+
+	public void testDeserializeTooManyPartsI() {
+		try {
+			ArtifactKey.parse(",,,,");
+			fail();
+		} catch (IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+
+	public void testDeserializeTooFewPartsII() {
+		try {
+			ArtifactKey.parse("namespace,classifier");
+			fail();
+		} catch (IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+}
Index: src/org/eclipse/equinox/p2/tests/artifact/optimizers/pack200/AbstractOptimizerTest.java
===================================================================
RCS file: src/org/eclipse/equinox/p2/tests/artifact/optimizers/pack200/AbstractOptimizerTest.java
diff -N src/org/eclipse/equinox/p2/tests/artifact/optimizers/pack200/AbstractOptimizerTest.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/equinox/p2/tests/artifact/optimizers/pack200/AbstractOptimizerTest.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.tests.artifact.optimizers.pack200;
+
+import java.io.*;
+import java.net.URL;
+import junit.framework.TestCase;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.equinox.internal.p2.artifact.optimizers.jardelta.Optimizer;
+import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
+import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
+import org.eclipse.equinox.p2.artifact.repository.*;
+import org.eclipse.equinox.p2.metadata.IArtifactKey;
+import org.eclipse.equinox.p2.tests.TestActivator;
+import org.eclipse.osgi.service.urlconversion.URLConverter;
+import org.osgi.framework.Version;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class AbstractOptimizerTest extends TestCase {
+	private ServiceTracker managerTracker;
+	private File workDir;
+
+	public AbstractOptimizerTest(String name) {
+		super(name);
+	}
+
+	public AbstractOptimizerTest() {
+		super("");
+	}
+
+	protected void setUp() throws Exception {
+		managerTracker = new ServiceTracker(TestActivator.getContext(), IArtifactRepositoryManager.class.getName(), null);
+		managerTracker.open();
+	}
+
+	protected void tearDown() throws Exception {
+		managerTracker.close();
+		if (workDir != null)
+			FileUtils.deleteAll(workDir);
+	}
+
+	public void testJarRoundTrip() {
+		URL repositoryJar = TestActivator.getContext().getBundle().getEntry("/testData/enginerepo.zip");
+		URL repositoryURL = extractRepositoryJAR(repositoryJar);
+		assertNotNull("Could not extract repository", repositoryURL);
+		IArtifactRepository repository = ((IArtifactRepositoryManager) managerTracker.getService()).loadRepository(repositoryURL, null);
+		IArtifactKey key = new ArtifactKey("eclipse", "plugin", "testdata", new Version("1.0.0.2"));
+		IArtifactDescriptor[] descriptors = repository.getArtifactDescriptors(key);
+		assertTrue("Artifact Descriptor for engine missing", descriptors.length == 1);
+
+		new Optimizer(repository, 1, 1).run();
+		descriptors = repository.getArtifactDescriptors(key);
+		assertTrue("Optimization was a no-op", descriptors.length == 2);
+
+		IArtifactDescriptor canonical = null;
+		IArtifactDescriptor optimized = null;
+		for (int i = 0; i < descriptors.length; i++) {
+			if (descriptors[i].getProcessingSteps().length == 0)
+				canonical = descriptors[i];
+			else
+				optimized = descriptors[i];
+		}
+
+		assertTrue("Optmized descriptor not found", optimized != null);
+		assertTrue("Canonical descriptor not found", canonical != null);
+		long optimizedSize = Long.parseLong(optimized.getProperty(IArtifactDescriptor.DOWNLOAD_SIZE));
+		long canonicalSize = Long.parseLong(canonical.getProperty(IArtifactDescriptor.DOWNLOAD_SIZE));
+		assertTrue("Optimzed not smaller than canonical", optimizedSize < canonicalSize);
+
+		File canonicalFile = fetchArtifact("canonical", canonical);
+		File optimizedFile = fetchArtifact("optimized", optimized);
+		compareFiles(canonicalFile, optimizedFile);
+	}
+
+	private URL extractRepositoryJAR(URL source) {
+		String filter = "(protocol=" + source.getProtocol() + ")";
+		URLConverter converter = (URLConverter) ServiceHelper.getService(TestActivator.getContext(), URLConverter.class.getName(), filter);
+		try {
+			if (converter == null)
+				return null;
+			URL repoURL = converter.toFileURL(source);
+			if (!repoURL.toExternalForm().endsWith(".jar") && !repoURL.toExternalForm().endsWith(".zip"))
+				return repoURL;
+			// else the repo is a JAR or zip and we should extract the contents to a work dir.
+			File repoLocation = getWorkDir();
+			FileUtils.unzipFile(new File(repoURL.getPath()), repoLocation);
+			return repoLocation.toURL();
+		} catch (IOException e) {
+			return null;
+		}
+	}
+
+	private void compareFiles(File canonicalFile, File optimizedFile) {
+		assertTrue("Canonical file does not exist", canonicalFile.exists());
+		assertTrue("Optimized file does not exist", optimizedFile.exists());
+		assertEquals(canonicalFile.length(), optimizedFile.length());
+		// TODO compare the actual content
+	}
+
+	private File fetchArtifact(String name, IArtifactDescriptor descriptor) {
+		try {
+			File result = new File(getWorkDir(), name);
+			OutputStream destination = new BufferedOutputStream(new FileOutputStream(result));
+			try {
+				descriptor.getRepository().getArtifact(descriptor, destination, new NullProgressMonitor());
+				return result;
+			} finally {
+				if (destination != null)
+					destination.close();
+			}
+		} catch (IOException e) {
+			fail("Could not fetch artifact " + descriptor);
+		}
+		return null;
+	}
+
+	private File getWorkDir() throws IOException {
+		if (workDir != null)
+			return workDir;
+		workDir = File.createTempFile("work", "");
+		if (!workDir.delete())
+			throw new IOException("Could not delete file for creating temporary working dir.");
+		if (!workDir.mkdirs())
+			throw new IOException("Could not create temporary working dir.");
+		return workDir;
+	}
+
+}
#P org.eclipse.equinox.p2.metadata
Index: src/org/eclipse/equinox/p2/metadata/IArtifactKey.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IArtifactKey.java,v
retrieving revision 1.1
diff -u -r1.1 IArtifactKey.java
--- src/org/eclipse/equinox/p2/metadata/IArtifactKey.java	30 Sep 2007 18:55:10 -0000	1.1
+++ src/org/eclipse/equinox/p2/metadata/IArtifactKey.java	12 Nov 2007 02:14:48 -0000
@@ -23,32 +23,32 @@
 public interface IArtifactKey {
 
 	/**
-	 * The namespace.
-	 * @return This returns the namespace segment of the artifact. Never
-	 *         null or empty.
+	 * Returns the namespace for this artifact key. The returned value can never be empty.
+	 * @return the namespace segment of the key.
 	 */
-	String getNamespace();
+	public String getNamespace();
 
 	/**
-	 * The classifier.
-	 * @return This returns the classifier segment of the key. Never
-	 *         null. Can be empty.
+	 * Returns the classifier for this artifact key. The returned value can be empty.
+	 * @return the classifier segment of the key.
 	 */
-	String getClassifier();
+	public String getClassifier();
 
 	/**
-	 * The identity of the artifact.
-	 * @return This returns the id segment of the artifact. Can
-	 *         be empty.
-	 *         
-	 * TODO: consider renaming this to getIdentity.
+	 * Returns the id for this artifact key. The returned value can be empty.
+	 * @return the classifier segment of the key.
 	 */
-	String getId();
+	public String getId();
 
 	/**
-	 * The version of the artifact.
-	 * @return This returns the version of the artifact. Never null. Can
-	 *         be empty (Version.emptyVersion).
+	 * Returns the version for this artifact key. 
+	 * @return the version segment of the key.
 	 */
-	Version getVersion();
+	public Version getVersion();
+
+	/**
+	 * Returns the canonical string form of this artifact key.
+	 * @return the canonical string representing this key
+	 */
+	public String toExternalForm();
 }
Index: src/org/eclipse/equinox/internal/p2/metadata/ArtifactKey.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ArtifactKey.java,v
retrieving revision 1.4
diff -u -r1.4 ArtifactKey.java
--- src/org/eclipse/equinox/internal/p2/metadata/ArtifactKey.java	9 Nov 2007 04:21:37 -0000	1.4
+++ src/org/eclipse/equinox/internal/p2/metadata/ArtifactKey.java	12 Nov 2007 02:14:48 -0000
@@ -10,6 +10,8 @@
  *******************************************************************************/
 package org.eclipse.equinox.internal.p2.metadata;
 
+import java.util.ArrayList;
+import java.util.StringTokenizer;
 import org.eclipse.core.runtime.Assert;
 import org.eclipse.equinox.p2.metadata.IArtifactKey;
 import org.osgi.framework.Version;
@@ -20,24 +22,61 @@
  * See {link IArtifact for a description of the lifecycle of artifact keys) 
  */
 public class ArtifactKey implements IArtifactKey {
-	private static final char SEP_CHAR = ',';
+	private static final String SEPARATOR = ","; //$NON-NLS-1$
 
 	private final String namespace;
 	private final String id;
 	private final String classifier;
 	private final Version version;
 
+	private static String[] getArrayFromList(String stringList, String separator) {
+		if (stringList == null || stringList.trim().length() == 0)
+			return new String[0];
+		ArrayList list = new ArrayList();
+		boolean separatorSeen = true;
+		StringTokenizer tokens = new StringTokenizer(stringList, separator, true);
+		while (tokens.hasMoreTokens()) {
+			String token = tokens.nextToken().trim();
+			if (token.equals(separator)) {
+				if (separatorSeen)
+					list.add("");
+				separatorSeen = true;
+			} else {
+				separatorSeen = false;
+				if (token.length() != 0)
+					list.add(token);
+			}
+		}
+		if (separatorSeen)
+			list.add(""); //$NON-NLS-1$
+		return (String[]) list.toArray(new String[list.size()]);
+	}
+
+	public static IArtifactKey parse(String specification) {
+		String[] parts = getArrayFromList(specification, SEPARATOR);
+		if (parts.length < 3 || parts.length > 4)
+			throw new IllegalArgumentException("Unexpected number of parts in artifact key: " + specification); //$NON-NLS-1$
+		Version version = Version.emptyVersion;
+		if (parts.length == 4 && parts[3].trim().length() > 0)
+			version = Version.parseVersion(parts[3]);
+		try {
+			return new ArtifactKey(parts[0], parts[1], parts[2], version);
+		} catch (IllegalArgumentException e) {
+			throw (IllegalArgumentException) new IllegalArgumentException("Wrong version syntax in artifact key: " + specification).initCause(e); //$NON-NLS-1$
+		}
+	}
+
 	public ArtifactKey(String namespace, String classifier, String id, Version version) {
 		super();
 		Assert.isNotNull(namespace);
 		Assert.isNotNull(classifier);
 		Assert.isNotNull(id);
 		Assert.isNotNull(version);
-		if (namespace.indexOf(SEP_CHAR) != -1)
+		if (namespace.indexOf(SEPARATOR) != -1)
 			throw new IllegalArgumentException("comma not allowed in namespace"); //$NON-NLS-1$
-		if (classifier.indexOf(SEP_CHAR) != -1)
+		if (classifier.indexOf(SEPARATOR) != -1)
 			throw new IllegalArgumentException("comma not allowed in classifier"); //$NON-NLS-1$
-		if (id.indexOf(SEP_CHAR) != -1)
+		if (id.indexOf(SEPARATOR) != -1)
 			throw new IllegalArgumentException("comma not allowed in id"); //$NON-NLS-1$
 		this.namespace = namespace;
 		this.classifier = classifier;
@@ -80,4 +119,12 @@
 		return id;
 	}
 
+	public String toExternalForm() {
+		StringBuffer data = new StringBuffer(namespace).append(SEPARATOR);
+		data.append(classifier).append(SEPARATOR);
+		data.append(id).append(SEPARATOR);
+		data.append(version.toString());
+		return data.toString();
+	}
+
 }
Index: META-INF/MANIFEST.MF
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.metadata/META-INF/MANIFEST.MF,v
retrieving revision 1.7
diff -u -r1.7 MANIFEST.MF
--- META-INF/MANIFEST.MF	9 Nov 2007 04:21:37 -0000	1.7
+++ META-INF/MANIFEST.MF	12 Nov 2007 02:14:48 -0000
@@ -5,7 +5,7 @@
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
 Bundle-Version: 0.1.0.qualifier
-Export-Package: org.eclipse.equinox.internal.p2.metadata;x-friends:="org.eclipse.equinox.p2.metadata.generator,org.eclipse.equinox.p2.metadata.repository,org.eclipse.equinox.p2.engine,org.eclipse.equinox.p2.artifact.repository,org.eclipse.equinox.p2.artifact.optimizers",
+Export-Package: org.eclipse.equinox.internal.p2.metadata;x-friends:="org.eclipse.equinox.p2.metadata.generator,org.eclipse.equinox.p2.metadata.repository,org.eclipse.equinox.p2.engine,org.eclipse.equinox.p2.artifact.repository,org.eclipse.equinox.p2.artifact.optimizers,org.eclipse.equinox.p2.artifact.processors",
  org.eclipse.equinox.p2.metadata,
  org.eclipse.equinox.p2.query,
  org.eclipse.equinox.p2.resolution
#P org.eclipse.equinox.p2.artifact.optimizers
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/ArtifactKeyDeSerializer.java
===================================================================
RCS file: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/ArtifactKeyDeSerializer.java
diff -N src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/ArtifactKeyDeSerializer.java
--- src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/ArtifactKeyDeSerializer.java	8 Nov 2007 17:01:00 -0000	1.2
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,120 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007 compeople AG and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * 	compeople AG (Stefan Liebig) - initial API and implementation
- *******************************************************************************/
-package org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff;
-
-import java.util.StringTokenizer;
-import org.eclipse.core.runtime.Assert;
-import org.eclipse.equinox.p2.metadata.IArtifactKey;
-import org.osgi.framework.Version;
-
-/**
- * The <code>ArtifactKeyDeSerializer</code> encapsulates the serialization and de-serialization
- * of artifact keys into string representation and vice versa.
- * This encoding pattern is used within the processing step descriptor's data property.<p>
- * <b>Note: </b>This class is a duplicate of org.eclipse.equinox.internal.p2.artifact.processor.jbdiff.ArtifactKeyDeSerializer.
- * This has been done because this class is only relevant for the delta stuff.   
- */
-public class ArtifactKeyDeSerializer {
-
-	private static final String EMPTY_STRING = ""; //$NON-NLS-1$
-	private static final String SEPARATOR = ","; //$NON-NLS-1$
-
-	public static IArtifactKey deserialize(String data) {
-
-		if (data == null || data.length() == 0)
-			throw new IllegalArgumentException("Artifact key could not be deserialized. Null or empty. Serialized key is " + data); //$NON-NLS-1$
-
-		String[] parts = new String[4];
-		StringTokenizer tokenizer = new StringTokenizer(data, SEPARATOR, true);
-		int i = 0;
-		int sepCount = 0;
-		boolean lastTokenWasSep = true;
-		while (tokenizer.hasMoreTokens()) {
-			String token = tokenizer.nextToken();
-			if (!token.equals(SEPARATOR)) {
-				parts[i++] = token;
-				lastTokenWasSep = false;
-				continue;
-			}
-			sepCount++;
-			if (lastTokenWasSep) {
-				parts[i++] = EMPTY_STRING;
-				continue;
-			}
-			lastTokenWasSep = true;
-		}
-
-		if (sepCount != 3)
-			throw new IllegalArgumentException("Artifact key could not be deserialized. Unexpected number of parts. Serialized key is " + data); //$NON-NLS-1$
-
-		if (i == 3)
-			parts[i++] = EMPTY_STRING;
-
-		try {
-			return new ArtifactKey(parts[0], parts[1], parts[2], Version.parseVersion(parts[3]));
-		} catch (IllegalArgumentException e) {
-			throw (IllegalArgumentException) new IllegalArgumentException("Artifact key could not be deserialized. Wrong version syntay. Serialized key is " + data).initCause(e); //$NON-NLS-1$
-		}
-	}
-
-	public static String serialize(IArtifactKey key) {
-		StringBuffer data = new StringBuffer(key.getNamespace()).append(SEPARATOR);
-		data.append(key.getClassifier()).append(SEPARATOR);
-		data.append(key.getId()).append(SEPARATOR);
-		data.append(key.getVersion().toString());
-		return data.toString();
-	}
-
-	private static class ArtifactKey implements IArtifactKey {
-
-		private final String namespace;
-		private final String id;
-		private final String classifier;
-		private final Version version;
-		private static final char SEP_CHAR = ',';
-
-		protected ArtifactKey(String namespace, String classifier, String id, Version version) {
-			super();
-			Assert.isNotNull(namespace);
-			Assert.isNotNull(classifier);
-			Assert.isNotNull(id);
-			Assert.isNotNull(version);
-			if (namespace.indexOf(SEP_CHAR) != -1)
-				throw new IllegalArgumentException("comma not allowed in namespace"); //$NON-NLS-1$
-			if (classifier.indexOf(SEP_CHAR) != -1)
-				throw new IllegalArgumentException("comma not allowed in classifier"); //$NON-NLS-1$
-			if (id.indexOf(SEP_CHAR) != -1)
-				throw new IllegalArgumentException("comma not allowed in id"); //$NON-NLS-1$
-			this.namespace = namespace;
-			this.classifier = classifier;
-			this.id = id;
-			this.version = version;
-		}
-
-		public String getClassifier() {
-			return classifier;
-		}
-
-		public String getId() {
-			return id;
-		}
-
-		public String getNamespace() {
-			return namespace;
-		}
-
-		public Version getVersion() {
-			return version;
-		}
-
-	}
-
-}
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/JBDiffZipStep.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.artifact.optimizers/src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/JBDiffZipStep.java,v
retrieving revision 1.1
diff -u -r1.1 JBDiffZipStep.java
--- src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/JBDiffZipStep.java	8 Nov 2007 13:19:06 -0000	1.1
+++ src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/JBDiffZipStep.java	12 Nov 2007 02:14:49 -0000
@@ -7,6 +7,7 @@
 *
 * Contributors:
 *  compeople AG (Stefan Liebig) - initial API and implementation
+*  IBM Corporation - ongoing development
 *******************************************************************************/
 package org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff;
 
@@ -25,37 +26,31 @@
 		super(repository);
 	}
 
-	protected void performDiff() throws IOException {
-		if (current == null)
-			// hmmm, no one wrote to this stream so there is nothing to pass on
-			return;
-		// Ok, so there is content, close stream
-		current.close();
-
-		try {
-			DirectByteArrayOutputStream sarredCurrent = new DirectByteArrayOutputStream();
-			SarUtil.zipToSar(current.getInputStream(), sarredCurrent);
-			current = null;
-			fetchPredecessor(new ArtifactDescriptor(key));
-			byte[] diff = JBDiff.bsdiff(predecessor.getBuffer(), predecessor.getBufferLength(), sarredCurrent.getBuffer(), sarredCurrent.getBufferLength());
-			predecessor = null;
-			sarredCurrent = null;
-			FileUtils.copyStream(new ByteArrayInputStream(diff), true, destination, false);
-		} finally {
-			predecessor = null;
-			current = null;
-		}
+	protected void performOptimization() throws IOException {
+		DirectByteArrayOutputStream sarredCurrent = new DirectByteArrayOutputStream();
+		SarUtil.zipToSar(((DirectByteArrayOutputStream) incomingStream).getInputStream(), sarredCurrent);
+		incomingStream = null;
+		DirectByteArrayOutputStream predecessor = fetchPredecessorBytes(new ArtifactDescriptor(key));
+		byte[] diff = JBDiff.bsdiff(predecessor.getBuffer(), predecessor.getBufferLength(), sarredCurrent.getBuffer(), sarredCurrent.getBufferLength());
+		// free up the memory as soon as possible.
+		predecessor = null;
+		incomingStream = null;
+		sarredCurrent = null;
+
+		// copy the result of the optimization to the destination.
+		FileUtils.copyStream(new ByteArrayInputStream(diff), true, destination, false);
 	}
 
-	private void fetchPredecessor(ArtifactDescriptor artifactDescriptor) throws IOException {
+	private DirectByteArrayOutputStream fetchPredecessorBytes(ArtifactDescriptor artifactDescriptor) throws IOException {
 		DirectByteArrayOutputStream zippedPredecessor = new DirectByteArrayOutputStream();
 
 		status = repository.getArtifact(artifactDescriptor, zippedPredecessor, monitor);
 		if (!status.isOK())
 			throw (IOException) new IOException(status.getMessage()).initCause(status.getException());
 
-		predecessor = new DirectByteArrayOutputStream();
-		SarUtil.zipToSar(zippedPredecessor.getInputStream(), predecessor);
+		DirectByteArrayOutputStream result = new DirectByteArrayOutputStream();
+		SarUtil.zipToSar(zippedPredecessor.getInputStream(), result);
+		return result;
 	}
 
 }
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/VersionLessArtifactKey.java
===================================================================
RCS file: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/VersionLessArtifactKey.java
diff -N src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/VersionLessArtifactKey.java
--- src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/VersionLessArtifactKey.java	8 Nov 2007 13:19:06 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,87 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007 compeople AG and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * 	compeople AG (Stefan Liebig) - initial API and implementation
- *******************************************************************************/
-/**
- * 
- */
-package org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff;
-
-import org.eclipse.equinox.p2.metadata.IArtifactKey;
-import org.osgi.framework.Version;
-
-final class VersionLessArtifactKey implements IArtifactKey {
-	private String classifier;
-	private String id;
-	private String namespace;
-
-	public VersionLessArtifactKey(IArtifactKey copyFrom) {
-		this.classifier = copyFrom.getClassifier();
-		this.id = copyFrom.getId();
-		this.namespace = copyFrom.getNamespace();
-	}
-
-	public String getClassifier() {
-		return classifier;
-	}
-
-	public String getId() {
-		return id;
-	}
-
-	public String getNamespace() {
-		return namespace;
-	}
-
-	public Version getVersion() {
-		return Version.emptyVersion;
-	}
-
-	/* (non-Javadoc)
-	 * @see java.lang.Object#hashCode()
-	 */
-	public int hashCode() {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + ((classifier == null) ? 0 : classifier.hashCode());
-		result = prime * result + ((id == null) ? 0 : id.hashCode());
-		result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
-		return result;
-	}
-
-	/* (non-Javadoc)
-	 * @see java.lang.Object#equals(java.lang.Object)
-	 */
-	public boolean equals(Object obj) {
-		if (this == obj)
-			return true;
-		if (obj == null)
-			return false;
-		if (!(obj instanceof VersionLessArtifactKey))
-			return false;
-		final VersionLessArtifactKey other = (VersionLessArtifactKey) obj;
-		if (classifier == null) {
-			if (other.classifier != null)
-				return false;
-		} else if (!classifier.equals(other.classifier))
-			return false;
-		if (id == null) {
-			if (other.id != null)
-				return false;
-		} else if (!id.equals(other.id))
-			return false;
-		if (namespace == null) {
-			if (other.namespace != null)
-				return false;
-		} else if (!namespace.equals(other.namespace))
-			return false;
-		return true;
-	}
-
-}
\ No newline at end of file
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/JBDiffStep.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.artifact.optimizers/src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/JBDiffStep.java,v
retrieving revision 1.1
diff -u -r1.1 JBDiffStep.java
--- src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/JBDiffStep.java	8 Nov 2007 13:19:06 -0000	1.1
+++ src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/JBDiffStep.java	12 Nov 2007 02:14:49 -0000
@@ -7,12 +7,13 @@
 *
 * Contributors:
 *  compeople AG (Stefan Liebig) - initial API and implementation
+*  IBM Corporation - ongoing development
 *******************************************************************************/
 package org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff;
 
 import ie.wombat.jbdiff.JBDiff;
 import java.io.*;
-import org.eclipse.core.runtime.Status;
+import org.eclipse.equinox.internal.p2.artifact.optimizers.AbstractDeltaStep;
 import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
 import org.eclipse.equinox.p2.artifact.repository.ArtifactDescriptor;
 import org.eclipse.equinox.p2.artifact.repository.IArtifactRepository;
@@ -21,60 +22,34 @@
 /**
  *
  */
-public class JBDiffStep extends AbstractDeltaDiffStep {
-
-	protected DirectByteArrayOutputStream current;
-	protected DirectByteArrayOutputStream predecessor;
+public class JBDiffStep extends AbstractDeltaStep {
 
 	public JBDiffStep(IArtifactRepository repository) {
 		super(repository);
 	}
 
-	public void write(int b) throws IOException {
-		OutputStream stream = getOutputStream();
-		stream.write(b);
+	protected OutputStream createIncomingStream() throws IOException {
+		return new DirectByteArrayOutputStream();
 	}
 
-	private OutputStream getOutputStream() {
-		if (current != null)
-			return current;
-
-		current = new DirectByteArrayOutputStream();
-		return current;
-	}
-
-	protected void performDiff() throws IOException {
-		if (current == null)
-			// hmmm, no one wrote to this stream so there is nothing to pass on
-			return;
-		// Ok, so there is content, close stream
-		current.close();
-
-		try {
-			fetchPredecessor(new ArtifactDescriptor(key));
-			byte[] diff = JBDiff.bsdiff(predecessor.getBuffer(), predecessor.getBufferLength(), current.getBuffer(), current.getBufferLength());
-			predecessor = null;
-			current = null;
-			FileUtils.copyStream(new ByteArrayInputStream(diff), true, destination, false);
-		} finally {
-			predecessor = null;
-			current = null;
-		}
-	}
-
-	public void close() throws IOException {
-		// When we go to close we must have seen all the content we are going to see
-		// So before closing, run unpack and write the unpacked result to the destination
-		performDiff();
-		super.close();
-		status = Status.OK_STATUS;
-	}
-
-	private void fetchPredecessor(ArtifactDescriptor artifactDescriptor) throws IOException {
-		predecessor = new DirectByteArrayOutputStream();
-		status = repository.getArtifact(artifactDescriptor, predecessor, monitor);
+	protected void performOptimization() throws IOException {
+		DirectByteArrayOutputStream predecessor = fetchPredecessorBytes(new ArtifactDescriptor(key));
+		DirectByteArrayOutputStream current = (DirectByteArrayOutputStream) incomingStream;
+		byte[] diff = JBDiff.bsdiff(predecessor.getBuffer(), predecessor.getBufferLength(), current.getBuffer(), current.getBufferLength());
+		// free up the memory as soon as possible.
+		predecessor = null;
+		current = null;
+		incomingStream = null;
+
+		// copy the result of the optimization to the destination.
+		FileUtils.copyStream(new ByteArrayInputStream(diff), true, destination, false);
+	}
+
+	private DirectByteArrayOutputStream fetchPredecessorBytes(ArtifactDescriptor artifactDescriptor) throws IOException {
+		DirectByteArrayOutputStream result = new DirectByteArrayOutputStream();
+		status = repository.getArtifact(artifactDescriptor, result, monitor);
 		if (!status.isOK())
 			throw (IOException) new IOException(status.getMessage()).initCause(status.getException());
+		return result;
 	}
-
 }
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/AbstractDeltaDiffStep.java
===================================================================
RCS file: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/AbstractDeltaDiffStep.java
diff -N src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/AbstractDeltaDiffStep.java
--- src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/AbstractDeltaDiffStep.java	9 Nov 2007 04:21:40 -0000	1.2
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,58 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007 compeople AG and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * 	compeople AG (Stefan Liebig) - initial API and implementation
- *******************************************************************************/
-package org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff;
-
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.equinox.internal.p2.artifact.optimizers.Activator;
-import org.eclipse.equinox.p2.artifact.repository.IArtifactDescriptor;
-import org.eclipse.equinox.p2.artifact.repository.IArtifactRepository;
-import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStep;
-import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStepDescriptor;
-import org.eclipse.equinox.p2.metadata.IArtifactKey;
-
-/**
- * The <code>AbstractDeltaDiffStep</code> is an abstract processing step that
- * retrieves a local artifact repository containing the serialized/encoded
- * artifact key. It assumes that the artifact key is stored within the data property
- * of the processing step descriptor and that is encoded with the <code>ArtifactKeySerializer</code>.
- */
-public abstract class AbstractDeltaDiffStep extends ProcessingStep {
-
-	protected IArtifactKey key;
-	protected IArtifactRepository repository;
-
-	protected AbstractDeltaDiffStep(IArtifactRepository repository) {
-		this.repository = repository;
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStep#initialize(org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStepDescriptor, org.eclipse.equinox.p2.artifact.repository.IArtifactDescriptor)
-	 */
-	public void initialize(ProcessingStepDescriptor descriptor, IArtifactDescriptor context) {
-		super.initialize(descriptor, context);
-
-		fetchArtifactKey(descriptor);
-	}
-
-	/**
-	 * Fetch the artifact key from the given processing step descriptor.
-	 * @param descriptor
-	 */
-	private void fetchArtifactKey(ProcessingStepDescriptor descriptor) {
-		try {
-			key = ArtifactKeyDeSerializer.deserialize(descriptor.getData());
-		} catch (IllegalArgumentException e) {
-			status = new Status(IStatus.ERROR, Activator.ID, "Predecessor artifact key for delta could not be deserialized. Serialized key is " + descriptor.getData(), e);
-		}
-	}
-
-}
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/Optimizer.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.artifact.optimizers/src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/Optimizer.java,v
retrieving revision 1.3
diff -u -r1.3 Optimizer.java
--- src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/Optimizer.java	9 Nov 2007 04:21:40 -0000	1.3
+++ src/org/eclipse/equinox/internal/p2/artifact/optimizers/jbdiff/Optimizer.java	12 Nov 2007 02:14:49 -0000
@@ -14,6 +14,7 @@
 import java.io.OutputStream;
 import java.util.*;
 import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.equinox.internal.p2.artifact.optimizers.VersionlessArtifactKey;
 import org.eclipse.equinox.p2.artifact.repository.*;
 import org.eclipse.equinox.p2.artifact.repository.processing.*;
 import org.eclipse.equinox.p2.metadata.IArtifactKey;
@@ -135,7 +136,7 @@
 	private IArtifactKey[][] getSortedRelatedArtifactKeys(IArtifactKey[] artifactKeys) {
 		Map map = new HashMap();
 		for (int i = 0; i < artifactKeys.length; i++) {
-			IArtifactKey freeKey = new VersionLessArtifactKey(artifactKeys[i]);
+			IArtifactKey freeKey = new VersionlessArtifactKey(artifactKeys[i]);
 			List values = (List) map.get(freeKey);
 			if (values == null) {
 				values = new ArrayList();
@@ -173,7 +174,7 @@
 		for (int i = 0; i < minDepth; i++) {
 
 			System.out.println("\t with " + strategy + " against " + descriptors[i].getArtifactKey());
-			String predecessorData = ArtifactKeyDeSerializer.serialize(descriptors[i].getArtifactKey());
+			String predecessorData = descriptors[i].getArtifactKey().toExternalForm();
 			ArtifactDescriptor newDescriptor = new ArtifactDescriptor(complete);
 			ProcessingStepDescriptor patchStep = new ProcessingStepDescriptor(strategy, predecessorData, true);
 			ProcessingStepDescriptor[] steps = new ProcessingStepDescriptor[] {patchStep};
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jardelta/JarDeltaOptimizerStep.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.artifact.optimizers/src/org/eclipse/equinox/internal/p2/artifact/optimizers/jardelta/JarDeltaOptimizerStep.java,v
retrieving revision 1.1
diff -u -r1.1 JarDeltaOptimizerStep.java
--- src/org/eclipse/equinox/internal/p2/artifact/optimizers/jardelta/JarDeltaOptimizerStep.java	9 Nov 2007 04:21:40 -0000	1.1
+++ src/org/eclipse/equinox/internal/p2/artifact/optimizers/jardelta/JarDeltaOptimizerStep.java	12 Nov 2007 02:14:49 -0000
@@ -14,109 +14,63 @@
 import java.io.*;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
+import org.eclipse.equinox.internal.p2.artifact.optimizers.AbstractDeltaStep;
 import org.eclipse.equinox.internal.p2.artifact.optimizers.Activator;
-import org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff.AbstractDeltaDiffStep;
 import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
-import org.eclipse.equinox.p2.artifact.repository.*;
+import org.eclipse.equinox.p2.artifact.repository.ArtifactDescriptor;
+import org.eclipse.equinox.p2.artifact.repository.IArtifactRepository;
 
 /**
- * The Pack200Packer expects an input containing normal ".jar" data.   
+ * The JAR delta expects an input containing normal ".jar" data.   
  */
-public class JarDeltaOptimizerStep extends AbstractDeltaDiffStep {
-	private static final String JAR_SUFFIX = ".jar"; //$NON-NLS-1$
+public class JarDeltaOptimizerStep extends AbstractDeltaStep {
 
 	private File incoming;
-	private OutputStream tempStream;
 
 	protected JarDeltaOptimizerStep(IArtifactRepository repository) {
 		super(repository);
 	}
 
-	public void write(int b) throws IOException {
-		OutputStream stream = getOutputStream();
-		stream.write(b);
+	protected OutputStream createIncomingStream() throws IOException {
+		incoming = File.createTempFile(INCOMING_ROOT, JAR_SUFFIX);
+		return new BufferedOutputStream(new FileOutputStream(incoming));
 	}
 
-	private OutputStream getOutputStream() throws IOException {
-		if (tempStream != null)
-			return tempStream;
-		// store input stream in temporary file
-		incoming = File.createTempFile("p2.jardelta.optimizer.incoming", JAR_SUFFIX);
-		tempStream = new BufferedOutputStream(new FileOutputStream(incoming));
-		return tempStream;
+	protected void cleanupTempFiles() {
+		super.cleanupTempFiles();
+		if (incoming != null)
+			incoming.delete();
 	}
 
-	private void performOptimization() throws IOException {
-		File predecessor = null;
+	protected void performOptimization() throws IOException {
 		File resultFile = null;
 		try {
-			// get the predecessor and perform the optimization into a temp file
-			predecessor = fetchPredecessor(new ArtifactDescriptor(key));
-			resultFile = File.createTempFile("p2.jardelta.optimizer.result", JAR_SUFFIX);
-			new DeltaComputer(predecessor, incoming, resultFile).run();
-
+			resultFile = optimize();
 			// now write the optimized content to the destination
 			if (resultFile.length() > 0) {
 				InputStream resultStream = new BufferedInputStream(new FileInputStream(resultFile));
 				FileUtils.copyStream(resultStream, true, destination, false);
 			} else {
-				status = new Status(IStatus.ERROR, Activator.ID, "Empty optimized file: " + resultFile);
+				status = new Status(IStatus.ERROR, Activator.ID, "Empty optimized file: " + resultFile); //$NON-NLS-1$
 			}
 		} finally {
-			// if we have a predecessor and it is our temp file then clean up the file
-			if (predecessor != null && predecessor.getAbsolutePath().indexOf("p2.jardelta.predecessor") > -1)
-				predecessor.delete();
 			if (resultFile != null)
 				resultFile.delete();
 		}
 	}
 
-	private File fetchPredecessor(ArtifactDescriptor descriptor) {
-		if (repository instanceof IFileArtifactRepository)
-			return ((IFileArtifactRepository) repository).getArtifactFile(descriptor);
-		File result = null;
-		OutputStream resultStream = null;
-		try {
-			try {
-				result = File.createTempFile("p2.jardelta.predecessor", JAR_SUFFIX);
-				resultStream = new BufferedOutputStream(new FileOutputStream(result));
-				status = repository.getArtifact(descriptor, resultStream, monitor);
-				return result;
-			} finally {
-				if (resultStream != null)
-					resultStream.close();
-			}
-		} catch (IOException e) {
-		}
-		return null;
-	}
-
-	public void close() throws IOException {
-		// When we go to close we must have seen all the content we are going to see.
-		// If no one wrote to the temp stream then there is nothing to do.  Be sure to delete the 
-		// the temporary file if any.
-		if (tempStream == null) {
-			if (incoming != null)
-				incoming.delete();
-			return;
-		}
-
-		// So there is content.  Close the temporary stream and perform the optimization.
-		// Performing the optimization should result in the new content being written to 
-		// the destination.  Make sure we delete the temporary file if any.
+	protected File optimize() throws IOException {
+		File predecessor = null;
 		try {
-			tempStream.close();
-			performOptimization();
+			File resultFile = File.createTempFile(RESULT_ROOT, JAR_SUFFIX);
+			// get the predecessor and perform the optimization into a temp file
+			predecessor = fetchPredecessor(new ArtifactDescriptor(key));
+			new DeltaComputer(predecessor, incoming, resultFile).run();
+			return resultFile;
 		} finally {
-			if (incoming != null)
-				incoming.delete();
+			// if we have a predecessor and it is our temp file then clean up the file
+			if (predecessor != null && predecessor.getAbsolutePath().indexOf(PREDECESSOR_ROOT) > -1)
+				predecessor.delete();
 		}
-
-		super.close();
-		// TODO need to get real status here.  sometimes the optimizers do not give 
-		// any reasonable return status
-		if (status == null)
-			status = Status.OK_STATUS;
 	}
-
 }
\ No newline at end of file
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/jardelta/Optimizer.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.artifact.optimizers/src/org/eclipse/equinox/internal/p2/artifact/optimizers/jardelta/Optimizer.java,v
retrieving revision 1.1
diff -u -r1.1 Optimizer.java
--- src/org/eclipse/equinox/internal/p2/artifact/optimizers/jardelta/Optimizer.java	9 Nov 2007 04:21:40 -0000	1.1
+++ src/org/eclipse/equinox/internal/p2/artifact/optimizers/jardelta/Optimizer.java	12 Nov 2007 02:14:49 -0000
@@ -15,7 +15,6 @@
 import java.io.OutputStream;
 import java.util.*;
 import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff.ArtifactKeyDeSerializer;
 import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
 import org.eclipse.equinox.p2.artifact.repository.*;
 import org.eclipse.equinox.p2.artifact.repository.processing.*;
@@ -163,7 +162,7 @@
 		int minDepth = Math.min(depth, descriptors.length);
 		for (int i = 0; i < minDepth; i++) {
 			System.out.println("\t with jar delta against " + descriptors[i].getArtifactKey());
-			String predecessorData = ArtifactKeyDeSerializer.serialize(descriptors[i].getArtifactKey());
+			String predecessorData = descriptors[i].getArtifactKey().toExternalForm();
 			ArtifactDescriptor newDescriptor = new ArtifactDescriptor(canonical);
 			ProcessingStepDescriptor patchStep = new ProcessingStepDescriptor(JAR_DELTA_PATCH_STEP, predecessorData, true);
 			ProcessingStepDescriptor[] steps = new ProcessingStepDescriptor[] {patchStep};
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/pack200/Pack200Step.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.artifact.optimizers/src/org/eclipse/equinox/internal/p2/artifact/optimizers/pack200/Pack200Step.java,v
retrieving revision 1.3
diff -u -r1.3 Pack200Step.java
--- src/org/eclipse/equinox/internal/p2/artifact/optimizers/pack200/Pack200Step.java	9 Nov 2007 04:21:40 -0000	1.3
+++ src/org/eclipse/equinox/internal/p2/artifact/optimizers/pack200/Pack200Step.java	12 Nov 2007 02:14:49 -0000
@@ -7,102 +7,67 @@
 *
 * Contributors:
 * 	compeople AG (Stefan Liebig) - initial API and implementation
-*  IBM - continuing development
+*  IBM Corporation - ongoing development
 *******************************************************************************/
 package org.eclipse.equinox.internal.p2.artifact.optimizers.pack200;
 
 import java.io.*;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
+import org.eclipse.equinox.internal.p2.artifact.optimizers.AbstractOptimizerStep;
 import org.eclipse.equinox.internal.p2.artifact.optimizers.Activator;
 import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
-import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStep;
 import org.eclipse.equinox.p2.jarprocessor.JarProcessorExecutor;
 import org.eclipse.equinox.p2.jarprocessor.JarProcessorExecutor.Options;
 
 /**
  * The Pack200Packer expects an input containing normal ".jar" data.   
  */
-public class Pack200Step extends ProcessingStep {
+public class Pack200Step extends AbstractOptimizerStep {
 	private static final String PACKED_SUFFIX = ".pack.gz"; //$NON-NLS-1$
-	private static final String JAR_SUFFIX = ".jar"; //$NON-NLS-1$
+	private File incoming;
 
-	private File source;
-	private OutputStream tempStream;
-
-	public void write(int b) throws IOException {
-		OutputStream stream = getOutputStream();
-		stream.write(b);
+	protected OutputStream createIncomingStream() throws IOException {
+		incoming = File.createTempFile(INCOMING_ROOT, JAR_SUFFIX);
+		return new BufferedOutputStream(new FileOutputStream(incoming));
 	}
 
-	private OutputStream getOutputStream() throws IOException {
-		if (tempStream != null)
-			return tempStream;
-		// store input stream in temporary file
-		source = File.createTempFile("p2.pack200", JAR_SUFFIX);
-		tempStream = new BufferedOutputStream(new FileOutputStream(source));
-		return tempStream;
+	protected void cleanupTempFiles() {
+		super.cleanupTempFiles();
+		if (incoming != null)
+			incoming.delete();
 	}
 
-	private void performPack() throws IOException {
-		BufferedInputStream resultStream = null;
+	protected void performOptimization() throws IOException {
 		File resultFile = null;
-		File workDir = null;
 		try {
-			if (tempStream == null)
-				// hmmm, no one wrote to this stream so there is nothing to pass on
-				return;
-			// Ok, so there is content, close the tempStream
-			tempStream.close();
-			// now create a temporary directory for the JarProcessor to work in
-			// TODO How to create a unique, temporary directory atomically?
-			workDir = File.createTempFile("p2.pack200.", "");
-			if (!workDir.delete())
-				throw new IOException("Could not delete file for creating temporary working dir.");
-			if (!workDir.mkdirs())
-				throw new IOException("Could not create temporary working dir.");
-
-			// unpack
-			Options options = new Options();
-			options.pack = true;
-			// TODO use false here assuming that all content is conditioned.  Need to revise this
-			options.processAll = false;
-			options.input = source;
-			options.outputDir = workDir.getPath();
-			options.verbose = true;
-			new JarProcessorExecutor().runJarProcessor(options);
-
-			// now write the packed content to our destination
-			resultFile = new File(workDir, source.getName() + PACKED_SUFFIX);
+			resultFile = optimize();
+			// now write the optimized content to the destination
 			if (resultFile.length() > 0) {
-				resultStream = new BufferedInputStream(new FileInputStream(resultFile));
+				InputStream resultStream = new BufferedInputStream(new FileInputStream(resultFile));
 				FileUtils.copyStream(resultStream, true, destination, false);
 			} else {
-				status = new Status(IStatus.ERROR, Activator.ID, "Empty file packed: " + resultFile);
+				status = new Status(IStatus.ERROR, Activator.ID, "Empty optimized file: " + resultFile); //$NON-NLS-1$
 			}
 		} finally {
-			if (source != null)
-				source.delete();
 			if (resultFile != null)
 				resultFile.delete();
-			if (workDir != null) {
-				FileUtils.deleteAll(workDir);
-				// TODO try twice since there seems to be some cases where the dir is not 
-				// deleted the first time.  At least on Windows...
-				FileUtils.deleteAll(workDir);
-			}
 		}
 	}
 
-	public void close() throws IOException {
-		// When we go to close we must have seen all the content we are going to see
-		// So before closing, run unpack and write the unpacked result to the destination
-		performPack();
-		super.close();
-		// TODO need to get real status here but curently the JAR processor does not give
-		// any reasonable return status
-		if (status == null)
-			status = Status.OK_STATUS;
+	protected File optimize() throws IOException {
+		try {
+			// unpack
+			Options options = new Options();
+			options.pack = true;
+			// TODO use false here assuming that all content is conditioned.  Need to revise this
+			options.processAll = true;
+			options.input = incoming;
+			options.outputDir = getWorkDir().getPath();
+			options.verbose = true;
+			new JarProcessorExecutor().runJarProcessor(options);
+			return new File(getWorkDir(), incoming.getName() + PACKED_SUFFIX);
+		} finally {
+		}
 	}
-
 }
\ No newline at end of file
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/AbstractDeltaStep.java
===================================================================
RCS file: src/org/eclipse/equinox/internal/p2/artifact/optimizers/AbstractDeltaStep.java
diff -N src/org/eclipse/equinox/internal/p2/artifact/optimizers/AbstractDeltaStep.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/equinox/internal/p2/artifact/optimizers/AbstractDeltaStep.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2007 compeople AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * 	compeople AG (Stefan Liebig) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.artifact.optimizers;
+
+import java.io.*;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
+import org.eclipse.equinox.p2.artifact.repository.*;
+import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStepDescriptor;
+import org.eclipse.equinox.p2.metadata.IArtifactKey;
+
+/**
+ * The <code>AbstractDeltaDiffStep</code> is an abstract processing step that
+ * retrieves a local artifact repository containing the serialized/encoded
+ * artifact key. It assumes that the artifact key is stored within the data property
+ * of the processing step descriptor and that is encoded with the <code>ArtifactKeySerializer</code>.
+ */
+public abstract class AbstractDeltaStep extends AbstractOptimizerStep {
+
+	protected IArtifactKey key;
+	protected IArtifactRepository repository;
+
+	protected AbstractDeltaStep(IArtifactRepository repository) {
+		this.repository = repository;
+	}
+
+	public void initialize(ProcessingStepDescriptor descriptor, IArtifactDescriptor context) {
+		super.initialize(descriptor, context);
+		readArtifactKey(descriptor);
+	}
+
+	private void readArtifactKey(ProcessingStepDescriptor descriptor) {
+		try {
+			key = ArtifactKey.parse(descriptor.getData());
+		} catch (IllegalArgumentException e) {
+			status = new Status(IStatus.ERROR, Activator.ID, "Predecessor artifact key for delta could not be deserialized. Serialized key is " + descriptor.getData(), e);
+		}
+	}
+
+	protected File fetchPredecessor(ArtifactDescriptor descriptor) {
+		if (repository instanceof IFileArtifactRepository)
+			return ((IFileArtifactRepository) repository).getArtifactFile(descriptor);
+		File result = null;
+		OutputStream resultStream = null;
+		try {
+			try {
+				result = File.createTempFile(PREDECESSOR_ROOT, JAR_SUFFIX);
+				resultStream = new BufferedOutputStream(new FileOutputStream(result));
+				status = repository.getArtifact(descriptor, resultStream, monitor);
+				return result;
+			} finally {
+				if (resultStream != null)
+					resultStream.close();
+			}
+		} catch (IOException e) {
+		}
+		return null;
+	}
+}
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/VersionlessArtifactKey.java
===================================================================
RCS file: src/org/eclipse/equinox/internal/p2/artifact/optimizers/VersionlessArtifactKey.java
diff -N src/org/eclipse/equinox/internal/p2/artifact/optimizers/VersionlessArtifactKey.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/equinox/internal/p2/artifact/optimizers/VersionlessArtifactKey.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2007 compeople AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * 	compeople AG (Stefan Liebig) - initial API and implementation
+ *******************************************************************************/
+/**
+ * 
+ */
+package org.eclipse.equinox.internal.p2.artifact.optimizers;
+
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
+import org.eclipse.equinox.p2.metadata.IArtifactKey;
+import org.osgi.framework.Version;
+
+public class VersionlessArtifactKey extends ArtifactKey {
+
+	public VersionlessArtifactKey(String namespace, String classifier, String id) {
+		super(namespace, classifier, id, Version.emptyVersion);
+	}
+
+	public VersionlessArtifactKey(IArtifactKey base) {
+		super(base.getNamespace(), base.getClassifier(), base.getId(), Version.emptyVersion);
+	}
+
+	public int hashCode() {
+		int hash = getId().hashCode();
+		hash = 17 * hash + getNamespace().hashCode();
+		hash = 17 * hash + getClassifier().hashCode();
+		return hash;
+	}
+
+	public boolean equals(Object obj) {
+		if (!(obj instanceof IArtifactKey))
+			return false;
+		IArtifactKey ak = (IArtifactKey) obj;
+		return ak.getId().equals(getId()) && ak.getNamespace().equals(getNamespace()) && ak.getClassifier().equals(getClassifier());
+	}
+}
Index: src/org/eclipse/equinox/internal/p2/artifact/optimizers/AbstractOptimizerStep.java
===================================================================
RCS file: src/org/eclipse/equinox/internal/p2/artifact/optimizers/AbstractOptimizerStep.java
diff -N src/org/eclipse/equinox/internal/p2/artifact/optimizers/AbstractOptimizerStep.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/equinox/internal/p2/artifact/optimizers/AbstractOptimizerStep.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,96 @@
+/*******************************************************************************
+* Copyright (c) 2007 compeople AG and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* 	compeople AG (Stefan Liebig) - initial API and implementation
+*  IBM Corporation - ongoing development
+*******************************************************************************/
+package org.eclipse.equinox.internal.p2.artifact.optimizers;
+
+import java.io.*;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
+import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStep;
+
+public abstract class AbstractOptimizerStep extends ProcessingStep {
+	protected static final String JAR_SUFFIX = ".jar"; //$NON-NLS-1$
+	protected static final String INCOMING_ROOT = "p2.optimizers.incoming"; //$NON-NLS-1$
+	protected static final String RESULT_ROOT = "p2.optimizers.result"; //$NON-NLS-1$
+	protected static final String PREDECESSOR_ROOT = "p2.optimizers.predecessor"; //$NON-NLS-1$
+
+	protected OutputStream incomingStream;
+	private File workDir;
+
+	protected AbstractOptimizerStep() {
+		super();
+	}
+
+	public void write(int b) throws IOException {
+		OutputStream stream = getOutputStream();
+		stream.write(b);
+	}
+
+	protected OutputStream getOutputStream() throws IOException {
+		if (incomingStream != null)
+			return incomingStream;
+		// if buffering, store input stream in temporary file
+		incomingStream = createIncomingStream();
+		return incomingStream;
+	}
+
+	protected abstract OutputStream createIncomingStream() throws IOException;
+
+	public void close() throws IOException {
+		// When we go to close we must have seen all the content we are going to see.
+		// If no one wrote to the temp stream then there is nothing to do. If there is 
+		// content then close the temporary stream and perform the optimization.
+		// Performing the optimization should result in the new content being written to 
+		// the destination.  Make sure we delete the temporary file if any.
+		try {
+			if (incomingStream != null) {
+				incomingStream.close();
+				performOptimization();
+			}
+		} finally {
+			incomingStream = null;
+			cleanupTempFiles();
+			cleanupWorkDir();
+		}
+
+		super.close();
+		// TODO need to get real status here.  sometimes the optimizers do not give 
+		// any reasonable return status
+		if (status == null)
+			status = Status.OK_STATUS;
+	}
+
+	protected abstract void performOptimization() throws IOException;
+
+	protected void cleanupTempFiles() {
+	}
+
+	private void cleanupWorkDir() throws IOException {
+		if (workDir != null) {
+			FileUtils.deleteAll(workDir);
+			// TODO try twice since there seems to be some cases where the dir is not 
+			// deleted the first time.  At least on Windows...
+			FileUtils.deleteAll(workDir);
+		}
+	}
+
+	protected File getWorkDir() throws IOException {
+		if (workDir != null)
+			return workDir;
+		workDir = File.createTempFile("work", "");
+		if (!workDir.delete())
+			throw new IOException("Could not delete file for creating temporary working dir.");
+		if (!workDir.mkdirs())
+			throw new IOException("Could not create temporary working dir.");
+		return workDir;
+	}
+
+}
#P org.eclipse.equinox.p2.artifact.processors
Index: src/org/eclipse/equinox/internal/p2/artifact/processors/jbdiff/AbstractDeltaPatchStep.java
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.artifact.processors/src/org/eclipse/equinox/internal/p2/artifact/processors/jbdiff/AbstractDeltaPatchStep.java,v
retrieving revision 1.2
diff -u -r1.2 AbstractDeltaPatchStep.java
--- src/org/eclipse/equinox/internal/p2/artifact/processors/jbdiff/AbstractDeltaPatchStep.java	9 Nov 2007 04:21:42 -0000	1.2
+++ src/org/eclipse/equinox/internal/p2/artifact/processors/jbdiff/AbstractDeltaPatchStep.java	12 Nov 2007 02:14:51 -0000
@@ -14,6 +14,7 @@
 import org.eclipse.core.runtime.Status;
 import org.eclipse.equinox.internal.p2.artifact.processors.Activator;
 import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
+import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
 import org.eclipse.equinox.p2.artifact.repository.*;
 import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStep;
 import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStepDescriptor;
@@ -49,7 +50,7 @@
 	 */
 	private void fetchArtifactKey(ProcessingStepDescriptor descriptor) {
 		try {
-			key = ArtifactKeyDeSerializer.deserialize(descriptor.getData());
+			key = ArtifactKey.parse(descriptor.getData());
 		} catch (IllegalArgumentException e) {
 			status = new Status(IStatus.ERROR, Activator.ID, "Predecessor artifact key for delta could not be deserialized. Serialized key is " + descriptor.getData(), e);
 		}
Index: src/org/eclipse/equinox/internal/p2/artifact/processors/jbdiff/ArtifactKeyDeSerializer.java
===================================================================
RCS file: src/org/eclipse/equinox/internal/p2/artifact/processors/jbdiff/ArtifactKeyDeSerializer.java
diff -N src/org/eclipse/equinox/internal/p2/artifact/processors/jbdiff/ArtifactKeyDeSerializer.java
--- src/org/eclipse/equinox/internal/p2/artifact/processors/jbdiff/ArtifactKeyDeSerializer.java	8 Nov 2007 17:00:59 -0000	1.2
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,120 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007 compeople AG and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * 	compeople AG (Stefan Liebig) - initial API and implementation
- *******************************************************************************/
-package org.eclipse.equinox.internal.p2.artifact.processors.jbdiff;
-
-import java.util.StringTokenizer;
-import org.eclipse.core.runtime.Assert;
-import org.eclipse.equinox.p2.metadata.IArtifactKey;
-import org.osgi.framework.Version;
-
-/**
- * The <code>ArtifactKeyDeSerializer</code> encapsulates the serialization and de-serialization
- * of artifact keys into string representation and vice versa.
- * This encoding pattern is used within the processing step descriptor´s data property.<p>
- * <b>Note: </b>This class is a duplicate of org.eclipse.equinox.internal.p2.artifact.optimizers.jbdiff.ArtifactKeyDeSerializer.
- * This has been done because this class is only relevant for the delta stuff.  
- */
-public class ArtifactKeyDeSerializer {
-
-	private static final String EMPTY_STRING = ""; //$NON-NLS-1$
-	private static final String SEPARATOR = ","; //$NON-NLS-1$
-
-	public static IArtifactKey deserialize(String data) {
-
-		if (data == null || data.length() == 0)
-			throw new IllegalArgumentException("Artifact key could not be deserialized. Null or empty. Serialized key is " + data); //$NON-NLS-1$
-
-		String[] parts = new String[4];
-		StringTokenizer tokenizer = new StringTokenizer(data, SEPARATOR, true);
-		int i = 0;
-		int sepCount = 0;
-		boolean lastTokenWasSep = true;
-		while (tokenizer.hasMoreTokens()) {
-			String token = tokenizer.nextToken();
-			if (!token.equals(SEPARATOR)) {
-				parts[i++] = token;
-				lastTokenWasSep = false;
-				continue;
-			}
-			sepCount++;
-			if (lastTokenWasSep) {
-				parts[i++] = EMPTY_STRING;
-				continue;
-			}
-			lastTokenWasSep = true;
-		}
-
-		if (sepCount != 3)
-			throw new IllegalArgumentException("Artifact key could not be deserialized. Unexpected number of parts. Serialized key is " + data); //$NON-NLS-1$
-
-		if (i == 3)
-			parts[i++] = EMPTY_STRING;
-
-		try {
-			return new ArtifactKey(parts[0], parts[1], parts[2], Version.parseVersion(parts[3]));
-		} catch (IllegalArgumentException e) {
-			throw (IllegalArgumentException) new IllegalArgumentException("Artifact key could not be deserialized. Wrong version syntay. Serialized key is " + data).initCause(e); //$NON-NLS-1$
-		}
-	}
-
-	public static String serialize(IArtifactKey key) {
-		StringBuffer data = new StringBuffer(key.getNamespace()).append(SEPARATOR);
-		data.append(key.getClassifier()).append(SEPARATOR);
-		data.append(key.getId()).append(SEPARATOR);
-		data.append(key.getVersion().toString());
-		return data.toString();
-	}
-
-	private static class ArtifactKey implements IArtifactKey {
-
-		private final String namespace;
-		private final String id;
-		private final String classifier;
-		private final Version version;
-		private static final char SEP_CHAR = ',';
-
-		protected ArtifactKey(String namespace, String classifier, String id, Version version) {
-			super();
-			Assert.isNotNull(namespace);
-			Assert.isNotNull(classifier);
-			Assert.isNotNull(id);
-			Assert.isNotNull(version);
-			if (namespace.indexOf(SEP_CHAR) != -1)
-				throw new IllegalArgumentException("comma not allowed in namespace"); //$NON-NLS-1$
-			if (classifier.indexOf(SEP_CHAR) != -1)
-				throw new IllegalArgumentException("comma not allowed in classifier"); //$NON-NLS-1$
-			if (id.indexOf(SEP_CHAR) != -1)
-				throw new IllegalArgumentException("comma not allowed in id"); //$NON-NLS-1$
-			this.namespace = namespace;
-			this.classifier = classifier;
-			this.id = id;
-			this.version = version;
-		}
-
-		public String getClassifier() {
-			return classifier;
-		}
-
-		public String getId() {
-			return id;
-		}
-
-		public String getNamespace() {
-			return namespace;
-		}
-
-		public Version getVersion() {
-			return version;
-		}
-
-	}
-
-}
Index: META-INF/MANIFEST.MF
===================================================================
RCS file: /cvsroot/eclipse/equinox-incubator/provisioning/org.eclipse.equinox.p2.artifact.processors/META-INF/MANIFEST.MF,v
retrieving revision 1.5
diff -u -r1.5 MANIFEST.MF
--- META-INF/MANIFEST.MF	9 Nov 2007 04:21:42 -0000	1.5
+++ META-INF/MANIFEST.MF	12 Nov 2007 02:14:51 -0000
@@ -12,6 +12,7 @@
 Import-Package: ie.wombat.jbdiff,
  org.eclipse.equinox.internal.p2.core.helpers,
  org.eclipse.equinox.internal.p2.jarprocessor,
+ org.eclipse.equinox.internal.p2.metadata,
  org.eclipse.equinox.p2.artifact.repository,
  org.eclipse.equinox.p2.artifact.repository.processing,
  org.eclipse.equinox.p2.core.repository,