You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ma...@apache.org on 2008/01/30 17:46:39 UTC

svn commit: r616813 [2/9] - in /felix/trunk/deploymentadmin: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/felix/ src/main/java/org/apache/felix/deploymentadmin/ src/main/java/org/apache/felix/de...

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ExplodingOutputtingInputStream.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ExplodingOutputtingInputStream.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ExplodingOutputtingInputStream.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ExplodingOutputtingInputStream.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,243 @@
+/*
+ * 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.felix.deploymentadmin;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.jar.Attributes.Name;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * This class will write all entries encountered in an inputstream to disk. An index of files written to disk is kept in an index file in the
+ * order they were encountered. Each file is compressed using GZIP. All the work is done on a separate thread.
+ */
+class ExplodingOutputtingInputStream extends OutputtingInputStream implements Runnable {
+
+    private final Thread m_task;
+    private final File m_contentDir;
+    private final File m_indexFile;
+    private final PipedInputStream m_input;
+
+    /**
+     * Creates an instance of this class.
+     *
+     * @param inputStream The input stream that will be written to disk as individual entries as it's read.
+     * @param indexFile File to be used to write the index of all encountered files.
+     * @param contentDir File to be used as the directory to hold all files encountered in the stream.
+     * @throws IOException If a problem occurs reading the stream resources.
+     */
+    public ExplodingOutputtingInputStream(InputStream inputStream, File indexFile, File contentDir) throws IOException {
+        this(inputStream, new PipedOutputStream(), indexFile,  contentDir);
+    }
+
+    public void close() throws IOException {
+        super.close();
+        waitFor();
+    }
+
+    private ExplodingOutputtingInputStream(InputStream inputStream, PipedOutputStream output, File index, File root) throws IOException {
+        super(inputStream, output);
+        m_contentDir = root;
+        m_indexFile = index;
+        m_input = new PipedInputStream(output);
+        m_task = new Thread(this, "LiQ - ExplodingIncomingThread");
+        m_task.start();
+    }
+
+    public void waitFor() {
+        try {
+            m_task.join();
+        }
+        catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    public void run() {
+        ZipInputStream input = null;
+        PrintWriter writer = null;
+        try {
+            input = new ZipInputStream(m_input);
+            writer = new PrintWriter(new FileWriter(m_indexFile));
+            for (ZipEntry entry = input.getNextEntry(); entry != null; entry = input.getNextEntry()) {
+                File current = new File(m_contentDir, entry.getName());
+                if (entry.isDirectory()) {
+                    current.mkdirs();
+                }
+                else {
+                    writer.println(entry.getName());
+                    File parent = current.getParentFile();
+                    if (parent != null) {
+                        parent.mkdirs();
+                    }
+                    OutputStream output = null;
+                    try {
+                        output = new GZIPOutputStream(new FileOutputStream(current));
+                        byte[] buffer = new byte[4096];
+                        for (int i = input.read(buffer); i > -1; i = input.read(buffer)) {
+                            output.write(buffer, 0, i);
+                        }
+                    }
+                    finally {
+                        output.close();
+                    }
+                }
+                input.closeEntry();
+                writer.flush();
+            }
+        } catch (IOException ex) {
+            // TODO: log this
+        }
+        finally {
+            if (input != null) {
+                try {
+                    input.close();
+                }
+                catch (IOException e) {
+                    // Not much we can do
+                }
+            }
+            if (writer != null) {
+                writer.close();
+            }
+        }
+    }
+
+
+    public static void replace(File target, File source) {
+        delete(target, true);
+        source.renameTo(target);
+    }
+
+    private static void delete(File root, boolean deleteRoot) {
+        if (root.isDirectory()) {
+            File[] childs = root.listFiles();
+            for (int i = 0; i < childs.length; i++) {
+                delete(childs[i], true);
+            }
+        }
+        if (deleteRoot) {
+            root.delete();
+        }
+    }
+
+    public static void merge(File targetIndex, File target, File sourceIndex, File source) throws IOException {
+        List targetFiles = readIndex(targetIndex);
+        List sourceFiles = readIndex(sourceIndex);
+        List result = new ArrayList(targetFiles);
+
+        File manifestFile = new File(source, (String) sourceFiles.remove(0));
+        Manifest resultManifest = new Manifest(new GZIPInputStream(new FileInputStream(manifestFile)));
+
+        resultManifest.getMainAttributes().remove(new Name(Constants.DEPLOYMENTPACKAGE_FIXPACK));
+
+        for (Iterator i = result.iterator(); i.hasNext();) {
+            String targetFile = (String) i.next();
+            if(!resultManifest.getEntries().containsKey(targetFile) && !targetFile.equals("META-INF/MANIFEST.MF")) {
+                i.remove();
+            }
+        }
+
+        for (Iterator iter = sourceFiles.iterator(); iter.hasNext();) {
+            String path = (String) iter.next();
+            if (targetFiles.contains(path)) {
+                (new File(target, path)).delete();
+            }
+            else {
+                result.add(path);
+            }
+            (new File(source, path)).renameTo(new File(target, path));
+        }
+
+        targetFiles.removeAll(sourceFiles);
+
+        for (Iterator iter = resultManifest.getEntries().keySet().iterator(); iter.hasNext();) {
+            String path = (String) iter.next();
+            Attributes sourceAttribute = (Attributes) resultManifest.getEntries().get(path);
+            if ("true".equals(sourceAttribute.remove(new Name(Constants.DEPLOYMENTPACKAGE_MISSING)))) {
+                targetFiles.remove(path);
+            }
+        }
+
+        for (Iterator iter = targetFiles.iterator(); iter.hasNext();) {
+            String path = (String) iter.next();
+            (new File(target, path)).delete();
+        }
+
+        GZIPOutputStream outputStream = new GZIPOutputStream(new FileOutputStream(new File(target, "META-INF/MANIFEST.MF")));
+        resultManifest.write(outputStream);
+        outputStream.close();
+        writeIndex(targetIndex, result);
+    }
+
+    public static List readIndex(File index) throws IOException {
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader(index));
+            List result = new ArrayList();
+            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+                result.add(line);
+            }
+            return result;
+        }
+        finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                }
+                catch (IOException e) {
+                    // Not much we can do
+                }
+            }
+        }
+    }
+
+    private static void writeIndex(File index, List input) throws IOException {
+        PrintWriter writer = null;
+        try {
+            writer = new PrintWriter(new FileWriter(index));
+            for (Iterator iterator = input.iterator(); iterator.hasNext();) {
+                writer.println(iterator.next());
+            }
+        }
+        finally {
+            if (writer != null) {
+                writer.close();
+            }
+        }
+    }
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/FileDeploymentPackage.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/FileDeploymentPackage.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/FileDeploymentPackage.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/FileDeploymentPackage.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,99 @@
+/*
+ * 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.felix.deploymentadmin;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.Manifest;
+import java.util.zip.GZIPInputStream;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.deploymentadmin.DeploymentException;
+
+/**
+ * Implementation of a <code>DeploymentPackage</code> that is persisted on disk.
+ */
+class FileDeploymentPackage extends AbstractDeploymentPackage {
+
+    private final List m_index;
+    private final File m_contentsDir;
+
+    /**
+     * Creates a new instance of a deployment package stored on disk.
+     *
+     * @param index Reference to the index file that contains the order in which all the resources of this deployment package were received
+     * @param packageDir Reference to the directory in which the index and package cotents are stored.
+     * @param bundleContext The bundle context
+     * @throws DeploymentException Thrown if the disk contents do not resemble a valid deployment package.
+     * @throws IOException Thrown if there was a problem reading the resources from disk.
+     */
+    public FileDeploymentPackage(File index, File packageDir, BundleContext bundleContext) throws DeploymentException, IOException {
+        this(ExplodingOutputtingInputStream.readIndex(index), packageDir, bundleContext);
+    }
+
+    private FileDeploymentPackage(List index, File packageDir, BundleContext bundleContext) throws DeploymentException, IOException {
+        super(new Manifest(new GZIPInputStream(new FileInputStream(new File(packageDir, (String) index.remove(0))))), bundleContext);
+        m_index = index;
+        m_contentsDir = packageDir;
+    }
+
+    public BundleInfoImpl[] getOrderedBundleInfos() {
+        List result = new ArrayList();
+        for(Iterator i = m_index.iterator(); i.hasNext();) {
+            AbstractInfo bundleInfo = getBundleInfoByPath((String) i.next());
+            if (bundleInfo != null) {
+                result.add(bundleInfo);
+            }
+        }
+        return (BundleInfoImpl[]) result.toArray(new BundleInfoImpl[result.size()]);
+    }
+
+    public InputStream getBundleStream(String symbolicName) throws IOException {
+        BundleInfoImpl bundleInfo = getBundleInfoByName(symbolicName);
+        if (bundleInfo != null) {
+            return new GZIPInputStream(new FileInputStream(new File(m_contentsDir, bundleInfo.getPath())));
+        }
+        return null;
+    }
+
+    public ResourceInfoImpl[] getOrderedResourceInfos() {
+        List result = new ArrayList();
+        for(Iterator i = m_index.iterator(); i.hasNext();) {
+            AbstractInfo resourceInfo = getResourceInfoByPath((String) i.next());
+            if (resourceInfo != null) {
+                result.add(resourceInfo);
+            }
+        }
+        return (ResourceInfoImpl[]) result.toArray(new ResourceInfoImpl[result.size()]);
+    }
+
+    public InputStream getCurrentEntryStream() {
+        throw new UnsupportedOperationException("Not implemented for file-based deployment package");
+    }
+
+    public AbstractInfo getNextEntry() throws IOException {
+        throw new UnsupportedOperationException("Not implemented for file-based deployment package");
+    }
+
+}
\ No newline at end of file

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/OutputtingInputStream.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/OutputtingInputStream.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/OutputtingInputStream.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/OutputtingInputStream.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,85 @@
+/*
+ * 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.felix.deploymentadmin;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This extension of the <code>InputStream</code> writes every byte that is read to an
+ * <code>OutputStream</code> of choice. The outputstream is closed automatically when
+ * the end of the inputstream is reached.
+ */
+public class OutputtingInputStream extends InputStream {
+
+    private final InputStream m_inputStream;
+    private final OutputStream m_outputStream;
+
+    /**
+     * Creates an instance of the <code>OutputtingInputStream</code>.
+     *
+     * @param inputStream The inputstream from which bytes will be read.
+     * @param outputStream The outputstream to which every byte that is read should be outputted.
+     */
+    public OutputtingInputStream(InputStream inputStream, OutputStream outputStream) {
+        super();
+        m_inputStream = inputStream;
+        m_outputStream = outputStream;
+    }
+
+    public int read() throws IOException {
+        int i = m_inputStream.read();
+        if (i != -1) {
+            m_outputStream.write(i);
+        }
+        return i;
+    }
+
+    public int read(byte[] buffer) throws IOException {
+        int i = m_inputStream.read(buffer);
+        if (i != -1) {
+            m_outputStream.write(buffer, 0, i);
+        }
+        return i;
+    }
+
+    public int read(byte[] buffer, int off, int len) throws IOException {
+        int i = m_inputStream.read(buffer, off, len);
+        if (i != -1) {
+            m_outputStream.write(buffer, off, i);
+        }
+        return i;
+    }
+
+    public void close() throws IOException {
+        try {
+            m_inputStream.close();
+        }
+        finally {
+            try {
+                m_outputStream.close();
+            }
+            catch (Exception e) {
+                // not much we can do
+            }
+        }
+    }
+
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ResourceInfoImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ResourceInfoImpl.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ResourceInfoImpl.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/ResourceInfoImpl.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,52 @@
+/*
+ * 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.felix.deploymentadmin;
+
+import java.util.jar.Attributes;
+
+import org.osgi.service.deploymentadmin.DeploymentException;
+
+/**
+ * This class represents the meta data of a processed resource as used by the Deployment Admin.
+ */
+public class ResourceInfoImpl extends AbstractInfo {
+
+    private String m_resourceProcessor;
+
+    /**
+     * Create an instance of this class.
+     *
+     * @param path String containing the path / resource-id of the processed resource.
+     * @param attributes Attributes containing the meta data of the resource.
+     * @throws DeploymentException If the specified attributes do not describe a processed resource.
+     */
+    public ResourceInfoImpl(String path, Attributes attributes) throws DeploymentException {
+        super(path, attributes);
+        m_resourceProcessor = attributes.getValue(Constants.RESOURCE_PROCESSOR);
+    }
+
+    /**
+     * Determines the resource processor for this processed resource.
+     *
+     * @return String containing the PID of the resource processor that should handle this processed resource.
+     */
+    public String getResourceProcessor() {
+        return m_resourceProcessor;
+    }
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/Semaphore.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/Semaphore.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/Semaphore.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/Semaphore.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,121 @@
+/*
+ * 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.felix.deploymentadmin;
+
+/**
+ * A semaphore, that maintains one single permit. An <code>acquire()</code> blocks until a permit is
+ * available, whilst <code>release()</code> will unblock it.
+ */
+public class Semaphore {
+    private boolean m_available;
+
+    /**
+     * Creates a new semaphore that is available.
+     */
+    public Semaphore() {
+        m_available = true;
+    }
+
+    /**
+     * Creates a new semaphore and allows you to specify if it's available or not.
+     *
+     * @param isAvailable should the semaphore be available or not
+     */
+    public Semaphore(boolean isAvailable) {
+        m_available = isAvailable;
+    }
+
+    /**
+     * Acquires the semaphore, or blocks until it's available or the thread is interrupted.
+     *
+     * @throws InterruptedException when the thread is interrupted
+     */
+    public void acquire() throws InterruptedException {
+        if (Thread.interrupted()) {
+            throw new InterruptedException();
+        }
+        synchronized (this) {
+            try {
+                if (!m_available) {
+                    wait();
+                }
+                m_available = false;
+            }
+            catch (InterruptedException ie) {
+                notify();
+                throw ie;
+            }
+        }
+    }
+
+    /**
+     * Tries to acquire the semaphore and waits for the duration of the specified timeout
+     * until it becomes available.
+     *
+     * @param timeout the number of milliseconds to wait
+     * @return <code>true</code> if the semaphore was acquired, <code>false</code> if it was
+     *     not after waiting for the specified amount of time
+     * @throws InterruptedException when the thread is interrupted
+     */
+    public boolean tryAcquire(long timeout) throws InterruptedException {
+        if (Thread.interrupted()) {
+            throw new InterruptedException();
+        }
+        synchronized (this) {
+            if (m_available) {
+                m_available = false;
+                return true;
+            }
+            else if (timeout <= 0) {
+                return false;
+            }
+            else {
+                long startTime = System.currentTimeMillis();
+                try {
+                    while (true) {
+                        wait(timeout);
+                        if (m_available) {
+                            m_available = false;
+                            return true;
+                        }
+                        else {
+                            timeout -= (System.currentTimeMillis() - startTime);
+                            if (timeout <= 0) {
+                                return false;
+                            }
+                        }
+                    }
+                }
+                catch (InterruptedException ie) {
+                    notify();
+                    throw ie;
+                }
+            }
+        }
+    }
+
+    /**
+     * Releases the semaphore. If threads were waiting, one of them is
+     * notified.
+     */
+    public synchronized void release() {
+        m_available = true;
+        notify();
+    }
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/StreamDeploymentPackage.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/StreamDeploymentPackage.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/StreamDeploymentPackage.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/StreamDeploymentPackage.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,98 @@
+/*
+ * 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.felix.deploymentadmin;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.JarInputStream;
+import java.util.zip.ZipEntry;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.deploymentadmin.DeploymentException;
+
+/**
+ * This class represents a deployment package that is read from a jar stream.
+ */
+class StreamDeploymentPackage extends AbstractDeploymentPackage {
+
+    private final JarInputStream m_input;
+    private final List m_names = new ArrayList();
+
+    /**
+     * Creates an instance of this class.
+     *
+     * @param input The stream from which the deployment package can be read.
+     * @param bundleContext The bundle context.
+     * @throws DeploymentException If it was not possible to read a valid deployment package from the specified stream.
+     */
+    public StreamDeploymentPackage(JarInputStream input, BundleContext bundleContext) throws DeploymentException {
+        super(input.getManifest(), bundleContext);
+        m_input = input;
+    }
+
+    public InputStream getBundleStream(String symbolicName) {
+        throw new UnsupportedOperationException("Not applicable for stream-based deployment package");
+    }
+
+    // This only works for those resources that have been read from the stream already, no guarantees for remainder of stream
+    public BundleInfoImpl[] getOrderedBundleInfos() {
+        List result = new ArrayList();
+
+        // add all bundle resources ordered by location in stream
+        for(Iterator i = m_names.iterator(); i.hasNext();) {
+            String indexEntry = (String) i.next();
+            AbstractInfo bundleInfo = getBundleInfoByPath(indexEntry);
+            if (bundleInfo != null) {
+                result.add(bundleInfo);
+            }
+        }
+
+        // add bundle resources marked missing to the end of the result
+        BundleInfoImpl[] bundleInfoImpls = getBundleInfoImpls();
+        for (int i = 0; i < bundleInfoImpls.length; i++) {
+            if(bundleInfoImpls[i].isMissing()) {
+                result.add(bundleInfoImpls[i]);
+            }
+        }
+        return (BundleInfoImpl[]) result.toArray(new BundleInfoImpl[result.size()]);
+    }
+
+    public ResourceInfoImpl[] getOrderedResourceInfos() {
+        throw new UnsupportedOperationException("Not applicable for stream-based deployment package");
+    }
+
+    public AbstractInfo getNextEntry() throws IOException {
+        ZipEntry nextEntry = m_input.getNextJarEntry();
+        if (nextEntry == null) {
+            return null;
+        }
+        String name = nextEntry.getName();
+        m_names.add(name);
+        AbstractInfo abstractInfoByPath = getAbstractInfoByPath(name);
+        return abstractInfoByPath;
+    }
+
+    public InputStream getCurrentEntryStream() {
+        return m_input;
+    }
+
+}
\ No newline at end of file

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/VersionRange.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/VersionRange.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/VersionRange.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/VersionRange.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,156 @@
+/*
+ * 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.felix.deploymentadmin;
+
+import org.osgi.framework.Version;
+
+/**
+ * This class represents a version range as defined in section 3.2.5 of the OSGi r4 specification.
+ */
+public class VersionRange {
+
+    public static final VersionRange infiniteRange = new VersionRange(Version.emptyVersion, true, null, true);
+
+    private Version m_low = null;
+    private boolean m_isLowInclusive = false;
+    private Version m_high = null;
+    private boolean m_isHighInclusive = false;
+    private String m_toString = null;
+
+    /**
+     * Create an instance of the VersionRange class.
+     *
+     * @param low Lower bound version
+     * @param isLowInclusive True if lower bound should be included in the range
+     * @param high Upper bound version
+     * @param isHighInclusive True if upper bound should be included in the range
+     */
+    public VersionRange(Version low, boolean isLowInclusive, Version high, boolean isHighInclusive) {
+        m_low = low;
+        m_isLowInclusive = isLowInclusive;
+        m_high = high;
+        m_isHighInclusive = isHighInclusive;
+    }
+
+    /**
+     * Creates an instance of the VersionRange class which resembles [version,*)
+     *
+     * @param version The lower boundary of the version range
+     */
+    public VersionRange(Version version) {
+        this(version, true, null, false);
+    }
+
+    /**
+     * Get the lower boundary of the version range, the boundary being inclusive or not is not taken into account.
+     *
+     * @return Version resembling the lower boundary of the version range
+     */
+    public Version getLow() {
+        return m_low;
+    }
+
+    /**
+     * Determines whether the lower boundary is inclusive or not.
+     *
+     * @return True if the lower boundary is inclusive, false otherwise.
+     */
+    public boolean isLowInclusive() {
+        return m_isLowInclusive;
+    }
+
+    /**
+     * Get the upper boundary of the version range, the boundary being inclusive or not is not taken in to account.
+     *
+     * @return Version resembling the upper boundary of the version range.
+     */
+    public Version getHigh() {
+        return m_high;
+    }
+
+    /**
+     * Determines whether the upper boundary is inclusive or not.
+     *
+     * @return True if the upper boundary is inclusive, false otherwise.
+     */
+    public boolean isHighInclusive() {
+        return m_isHighInclusive;
+    }
+
+    /**
+     * Determine if the specified version is part of the version range or not.
+     *
+     * @param version The version to verify
+     * @return True if the specified version is included in this version range, false otherwise.
+     */
+    public boolean isInRange(Version version) {
+        // We might not have an upper end to the range.
+        if (m_high == null) {
+            return (version.compareTo(m_low) >= 0);
+        }
+        else if (isLowInclusive() && isHighInclusive()) {
+            return (version.compareTo(m_low) >= 0) && (version.compareTo(m_high) <= 0);
+        }
+        else if (isHighInclusive()) {
+            return (version.compareTo(m_low) > 0) && (version.compareTo(m_high) <= 0);
+        }
+        else if (isLowInclusive()) {
+            return (version.compareTo(m_low) >= 0) && (version.compareTo(m_high) < 0);
+        }
+        return (version.compareTo(m_low) > 0) && (version.compareTo(m_high) < 0);
+    }
+
+    /**
+     * Parses a version range from the specified string.
+     *
+     * @param range String representation of the version range.
+     * @return A <code>VersionRange</code> object representing the version range.
+     * @throws IllegalArgumentException If <code>range</code> is improperly formatted.
+     */
+    public static VersionRange parse(String range) throws IllegalArgumentException {
+        // Check if the version is an interval.
+        if (range.indexOf(',') >= 0) {
+            String s = range.substring(1, range.length() - 1);
+            String vlo = s.substring(0, s.indexOf(',')).trim();
+            String vhi = s.substring(s.indexOf(',') + 1, s.length()).trim();
+            return new VersionRange(new Version(vlo), (range.charAt(0) == '['), new Version(vhi), (range.charAt(range.length() - 1) == ']'));
+        }
+        else {
+            return new VersionRange(new Version(range), true, null, false);
+        }
+    }
+
+    public String toString() {
+        if (m_toString == null) {
+            if (m_high != null) {
+                StringBuffer sb = new StringBuffer();
+                sb.append(m_isLowInclusive ? '[' : '(');
+                sb.append(m_low.toString());
+                sb.append(',');
+                sb.append(m_high.toString());
+                sb.append(m_isHighInclusive ? ']' : ')');
+                m_toString = sb.toString();
+            }
+            else {
+                m_toString = m_low.toString();
+            }
+        }
+        return m_toString;
+    }
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/Command.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/Command.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/Command.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/Command.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,114 @@
+/*
+ * 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.felix.deploymentadmin.spi;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.osgi.service.deploymentadmin.DeploymentException;
+
+/**
+ * Commands describe a group of tasks to be executed within the execution a deployment session.
+ * A command that has already executed can be rolled back and a command that is currently in progress
+ * can be signaled to stop it's activities by canceling it.
+ */
+public abstract class Command {
+
+    private final List m_rollback = new ArrayList();
+    private final List m_commit = new ArrayList();
+    private volatile boolean m_cancelled;
+
+    /**
+     * Executes the command, the specified <code>DeploymentSession</code> can be used to obtain various
+     * information about the deployment session which the command is part of.
+     *
+     * @param session The deployment session this command is part of.
+     * @throws DeploymentException Thrown if the command could not successfully execute.
+     */
+    public abstract void execute(DeploymentSessionImpl session) throws DeploymentException;
+
+    /**
+     * Rolls back all actions that were added through the <code>addRollback(Runnable r)</code> method (in reverse
+     * adding order). It is not guaranteed that the state of everything related to the command will be as if the
+     * command was never executed, a best effort should be made though.
+     */
+    public void rollback() {
+        for (ListIterator i = m_rollback.listIterator(); i.hasPrevious();) {
+            Runnable runnable = (Runnable) i.previous();
+            runnable.run();
+        }
+        cleanUp();
+    }
+
+    /**
+     * Commits all changes the command may have defined when it was executed by calling the <code>execute()</code> method.
+     */
+    protected void commit() {
+        for (ListIterator i = m_commit.listIterator(); i.hasPrevious();) {
+            Runnable runnable = (Runnable) i.previous();
+            runnable.run();
+        }
+        cleanUp();
+    }
+
+    private void cleanUp() {
+        m_rollback.clear();
+        m_commit.clear();
+        m_cancelled = false;
+    }
+
+    /**
+     * Determines if the command was canceled. This method should be used regularly by implementing classes to determine if
+     * their command was canceled, if so they should return as soon as possible from their operations.
+     *
+     * @return true if the command was canceled, false otherwise.
+     */
+    protected boolean isCancelled() {
+        return m_cancelled;
+    }
+
+    /**
+     * Adds an action to be executed in case of a roll back.
+     *
+     * @param runnable The runnable to be executed in case of a roll back.
+     */
+    protected void addRollback(Runnable runnable) {
+        m_rollback.add(runnable);
+    }
+
+    /**
+     * Adds an action to be executed in case of a commit
+     *
+     * @param runnable The runnable to be executes in case of a commit.
+     */
+    protected void addCommit(Runnable runnable) {
+        m_commit.add(runnable);
+    }
+
+    /**
+     * Sets the command to being cancelled, this does not have an immediate effect. Commands that are executing should
+     * check regularly if they were cancelled and if so they should make an effort to stop their operations as soon as possible
+     * followed by throwing an <code>DeploymentException.CODE_CANCELLED</code> exception.
+     */
+    public void cancel() {
+        m_cancelled = true;
+    }
+
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/CommitResourceCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/CommitResourceCommand.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/CommitResourceCommand.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/CommitResourceCommand.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,93 @@
+/*
+ * 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.felix.deploymentadmin.spi;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.osgi.service.deploymentadmin.DeploymentException;
+import org.osgi.service.deploymentadmin.spi.ResourceProcessor;
+import org.osgi.service.deploymentadmin.spi.ResourceProcessorException;
+import org.osgi.service.log.LogService;
+
+/**
+ * Command that commits all the resource processors that were added to the command.
+ */
+public class CommitResourceCommand extends Command implements Runnable {
+
+    private final List m_processors = new ArrayList();
+
+    public void execute(DeploymentSessionImpl session) throws DeploymentException {
+        for (ListIterator i = m_processors.listIterator(); i.hasPrevious();) {
+            ResourceProcessor processor = (ResourceProcessor) i.previous();
+            try {
+                processor.prepare();
+            }
+            catch (ResourceProcessorException e) {
+                session.getLog().log(LogService.LOG_ERROR, "Preparing commit for resource processor failed", e);
+                throw new DeploymentException(DeploymentException.CODE_OTHER_ERROR, "Preparing commit for resource processor failed", e);
+            }
+        }
+        for (ListIterator i = m_processors.listIterator(); i.hasPrevious();) {
+            ResourceProcessor processor = (ResourceProcessor) i.previous();
+            try {
+                processor.commit();
+            }
+            catch (Exception e) {
+                session.getLog().log(LogService.LOG_ERROR, "Committing resource processor '" + processor + "' failed", e);
+                // TODO Throw exception?
+            }
+        }
+    }
+
+    public void rollback() {
+        for (ListIterator i = m_processors.listIterator(); i.hasPrevious();) {
+            ResourceProcessor processor = (ResourceProcessor) i.previous();
+            try {
+                processor.rollback();
+            }
+            catch (Exception e) {
+                // TODO Log this?
+            }
+            i.remove();
+        }
+    }
+
+    /**
+     * Add a resource processor, all resource processors that are added will be committed when the command is executed.
+     *
+     * @param processor The resource processor to add.
+     */
+    public void addResourceProcessor(ResourceProcessor processor) {
+        for (Iterator i = m_processors.iterator(); i.hasNext();) {
+            ResourceProcessor proc = (ResourceProcessor) i.next();
+            if (proc == processor) {
+                return;
+            }
+        }
+        m_processors.add(processor);
+    }
+
+    public void run() {
+        rollback();
+    }
+
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DeploymentSessionImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DeploymentSessionImpl.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DeploymentSessionImpl.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DeploymentSessionImpl.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,178 @@
+/*
+ * 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.felix.deploymentadmin.spi;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.felix.deploymentadmin.AbstractDeploymentPackage;
+import org.apache.felix.deploymentadmin.DeploymentAdminImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.deploymentadmin.DeploymentException;
+import org.osgi.service.deploymentadmin.DeploymentPackage;
+import org.osgi.service.deploymentadmin.spi.DeploymentSession;
+import org.osgi.service.log.LogService;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class DeploymentSessionImpl implements DeploymentSession {
+
+    private final AbstractDeploymentPackage m_target;
+    private final AbstractDeploymentPackage m_source;
+    private final List m_commands;
+    private final DeploymentAdminImpl m_admin;
+    private volatile Command m_currentCommand = null;
+    private volatile boolean m_cancelled;
+
+    public DeploymentSessionImpl(AbstractDeploymentPackage source, AbstractDeploymentPackage target, List commands, DeploymentAdminImpl admin) {
+        m_source = source;
+        m_target = target;
+        m_commands = commands;
+        m_admin = admin;
+    }
+
+    /**
+     * Calling this method will cause the commands specified for this session to be executed. the commands will be rolled back if the session is
+     * canceled or if an exception is caused by one of the commands.
+     *
+     * @throws DeploymentException If the session was canceled (<code>DeploymentException.CODE_CANCELLED</code>) or if one of the commands caused an exception (<code>DeploymentException.*</code>)
+     */
+    public void call() throws DeploymentException {
+        List executedCommands = new ArrayList();
+        for (Iterator i = m_commands.iterator(); i.hasNext();) {
+            if (m_cancelled) {
+                // previous command did not pick up on cancel
+                rollback(executedCommands);
+                throw new DeploymentException(DeploymentException.CODE_CANCELLED);
+            }
+            m_currentCommand = (Command) i.next();
+            try {
+                executedCommands.add(m_currentCommand);
+                m_currentCommand.execute(this);
+            }
+            catch (DeploymentException de) {
+                rollback(executedCommands);
+                throw de;
+            }
+        }
+        for (Iterator i = m_commands.iterator(); i.hasNext();) {
+            ((Command) i.next()).commit();
+        }
+        m_currentCommand = null;
+    }
+
+    private void rollback(List executedCommands) {
+        for (ListIterator i = executedCommands.listIterator(); i.hasPrevious();) {
+            Command command = (Command) i.previous();
+            command.rollback();
+        }
+    }
+
+    /**
+     * Cancels the session if it is in progress.
+     *
+     * @return true if a session was in progress and now canceled, false otherwise.
+     */
+    public boolean cancel() {
+        m_cancelled = true;
+        if (m_currentCommand != null) {
+            m_currentCommand.cancel();
+            return true;
+        }
+        return false;
+    }
+
+    public File getDataFile(Bundle bundle) {
+        BundleContext context = null;
+        try {
+            Method getBundleContext = bundle.getClass().getDeclaredMethod("getBundleContext", null);
+            getBundleContext.setAccessible(true);
+            context = (BundleContext) getBundleContext.invoke(bundle, null);
+        }
+        catch (Exception ex) {
+            // TODO: log this
+        }
+        File result = null;
+        if (context != null) {
+            result = context.getDataFile("");
+        }
+        if (result == null) {
+            // TODO: log this
+            throw new IllegalStateException("");
+        }
+        return result;
+    }
+
+    public DeploymentPackage getSourceDeploymentPackage() {
+        return m_source;
+    }
+
+    public DeploymentPackage getTargetDeploymentPackage() {
+        return m_target;
+    }
+
+    /**
+     * Returns the bundle context of the bundle this class is part of.
+     *
+     * @return The <code>BundleContext</code>.
+     */
+    public BundleContext getBundleContext() {
+        return m_admin.getBundleContext();
+    }
+
+    /**
+     * Returns the currently present log service.
+     *
+     * @return The <code>LogService</code>.
+     */
+    public LogService getLog() {
+        return m_admin.getLog();
+    }
+
+    /**
+     * Returns the currently present package admin.
+     *
+     * @return The <code>PackageAdmin</code>
+     */
+    public PackageAdmin getPackageAdmin() {
+        return m_admin.getPackageAdmin();
+    }
+
+    /**
+     * Returns the target deployment package as an <code>AbstractDeploymentPackage</code>.
+     *
+     * @return The target deployment package of the session.
+     */
+    public AbstractDeploymentPackage getTargetAbstractDeploymentPackage() {
+        return m_target;
+    }
+
+    /**
+     * Returns the source deployment package as an <code>AbstractDeploymentPackage</code>.
+     *
+     * @return The source deployment packge of the session.
+     */
+    public AbstractDeploymentPackage getSourceAbstractDeploymentPackage() {
+        return m_source;
+    }
+}
\ No newline at end of file

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DropBundleCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DropBundleCommand.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DropBundleCommand.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DropBundleCommand.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,84 @@
+/*
+ * 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.felix.deploymentadmin.spi;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.felix.deploymentadmin.AbstractDeploymentPackage;
+import org.apache.felix.deploymentadmin.BundleInfoImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.service.deploymentadmin.DeploymentException;
+import org.osgi.service.log.LogService;
+
+/**
+ * Command that uninstalls bundles, if rolled back the bundles are restored.
+ */
+public class DropBundleCommand extends Command {
+
+    public void execute(DeploymentSessionImpl session) throws DeploymentException {
+        AbstractDeploymentPackage target = session.getTargetAbstractDeploymentPackage();
+        AbstractDeploymentPackage source = session.getSourceAbstractDeploymentPackage();
+        LogService log = session.getLog();
+
+        BundleInfoImpl[] orderedTargetBundles = target.getOrderedBundleInfos();
+        for (int i = orderedTargetBundles.length - 1; i >= 0; i--) {
+            BundleInfoImpl bundleInfo = orderedTargetBundles[i];
+            if (source.getBundleInfoByPath(bundleInfo.getPath()) == null) {
+                // stale bundle, save a copy for rolling back and uninstall it
+                String symbolicName = bundleInfo.getSymbolicName();
+                try {
+                    Bundle bundle = target.getBundle(symbolicName);
+                    bundle.uninstall();
+                    addRollback(new InstallBundleRunnable(bundle, target.getBundleStream(symbolicName), log));
+                }
+                catch (BundleException be) {
+                    log.log(LogService.LOG_WARNING, "Bundle '" + symbolicName + "' could not be uninstalled", be);
+                }
+                catch (IOException e) {
+                    log.log(LogService.LOG_WARNING, "Could not get bundle data stream for bundle '" + symbolicName + "'", e);
+                    throw new DeploymentException(DeploymentException.CODE_OTHER_ERROR, "Could not prepare rollback for uninstalling bundle '" + symbolicName + "'");
+                }
+            }
+        }
+    }
+
+    private static class InstallBundleRunnable implements Runnable {
+
+        private final InputStream m_bundleStream;
+        private final Bundle m_bundle;
+        private final LogService m_log;
+
+        public InstallBundleRunnable(Bundle bundle, InputStream bundleStream, LogService log) {
+            m_bundle = bundle;
+            m_bundleStream = bundleStream;
+            m_log = log;
+        }
+
+        public void run() {
+            try {
+                m_bundle.update(m_bundleStream);
+            }
+            catch (BundleException e) {
+                m_log.log(LogService.LOG_WARNING, "Could not rollback uninstallation of bundle '" + m_bundle.getSymbolicName() + "'", e);
+            }
+        }
+    }
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DropResourceCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DropResourceCommand.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DropResourceCommand.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/DropResourceCommand.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,74 @@
+/*
+ * 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.felix.deploymentadmin.spi;
+
+import org.apache.felix.deploymentadmin.AbstractDeploymentPackage;
+import org.apache.felix.deploymentadmin.ResourceInfoImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.deploymentadmin.spi.ResourceProcessor;
+import org.osgi.service.deploymentadmin.spi.ResourceProcessorException;
+import org.osgi.service.log.LogService;
+
+/**
+ * Command that drops resources.
+ */
+public class DropResourceCommand extends Command {
+
+    private final CommitResourceCommand m_commitCommand;
+
+    /**
+     * Creates an instance of this command. The commit command is used to make sure
+     * the resource processors used to drop resources will be committed at a later stage in the process.
+     *
+     * @param commitCommand The commit command that will be executed at a later stage in the process.
+     */
+    public DropResourceCommand(CommitResourceCommand commitCommand) {
+        m_commitCommand = commitCommand;
+        addRollback(m_commitCommand);
+    }
+
+    public void execute(DeploymentSessionImpl session) {
+        AbstractDeploymentPackage target = session.getTargetAbstractDeploymentPackage();
+        AbstractDeploymentPackage source = session.getSourceAbstractDeploymentPackage();
+        BundleContext context = session.getBundleContext();
+        LogService log = session.getLog();
+
+        ResourceInfoImpl[] orderedTargetResources = target.getOrderedResourceInfos();
+        for (int i = orderedTargetResources.length - 1; i >= 0; i--) {
+            ResourceInfoImpl resourceInfo = orderedTargetResources[i];
+            String path = resourceInfo.getPath();
+            if (source.getResourceInfoByPath(path) == null) {
+                ServiceReference ref = target.getResourceProcessor(path);
+                if (ref != null) {
+                    ResourceProcessor resourceProcessor = (ResourceProcessor) context.getService(ref);
+                    if (resourceProcessor != null) {
+                        try {
+                            m_commitCommand.addResourceProcessor(resourceProcessor);
+                            resourceProcessor.dropped(path);
+                        }
+                        catch (ResourceProcessorException e) {
+                            log.log(LogService.LOG_WARNING, "Not allowed to drop resource '" + path + "'", e);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/GetStorageAreaCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/GetStorageAreaCommand.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/GetStorageAreaCommand.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/GetStorageAreaCommand.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,70 @@
+/*
+ * 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.felix.deploymentadmin.spi;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.deploymentadmin.BundleInfo;
+import org.osgi.service.deploymentadmin.DeploymentException;
+import org.osgi.service.deploymentadmin.DeploymentPackage;
+import org.osgi.service.log.LogService;
+
+/**
+ * Command that determines the storage area's of all bundles in the source deployment
+ * package of a deployment session.
+ */
+public class GetStorageAreaCommand extends Command {
+
+    private final Map m_storageAreas = new HashMap();
+
+    public void execute(DeploymentSessionImpl session) throws DeploymentException {
+        DeploymentPackage target = session.getTargetDeploymentPackage();
+        BundleInfo[] infos = target.getBundleInfos();
+        for (int i = 0; i < infos.length; i++) {
+            if (isCancelled()) {
+                throw new DeploymentException(DeploymentException.CODE_CANCELLED);
+            }
+            Bundle bundle = target.getBundle(infos[i].getSymbolicName());
+            if (bundle != null) {
+                try {
+                    File root = session.getDataFile(bundle);
+                    m_storageAreas.put(bundle.getSymbolicName(), root);
+                }
+                catch (IllegalStateException ise) {
+                    session.getLog().log(LogService.LOG_WARNING, "Could not get reference to storage area of bundle '" + bundle.getSymbolicName() +"'");
+                }
+            }
+        }
+    }
+
+    /**
+     * Determines the storage area's of all bundles in the source deployment package of
+     * a deployment session.
+     *
+     * @return <code>Map</code> with <code>File</code> object references to the storage area's, they bundle symbolic name is used as a key in the <code>Map</code>.
+     */
+    public Map getStorageAreas() {
+        return m_storageAreas;
+    }
+
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/ProcessResourceCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/ProcessResourceCommand.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/ProcessResourceCommand.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/ProcessResourceCommand.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,122 @@
+/*
+ * 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.felix.deploymentadmin.spi;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.deploymentadmin.AbstractDeploymentPackage;
+import org.apache.felix.deploymentadmin.AbstractInfo;
+import org.apache.felix.deploymentadmin.ResourceInfoImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.deploymentadmin.DeploymentException;
+import org.osgi.service.deploymentadmin.spi.ResourceProcessor;
+import org.osgi.service.deploymentadmin.spi.ResourceProcessorException;
+
+/**
+ * Command that processes all the processed resources in the source deployment package
+ * of a deployment session by finding their Resource Processors and having those process
+ * the resources.
+ */
+public class ProcessResourceCommand extends Command {
+
+    private final CommitResourceCommand m_commitCommand;
+
+    /**
+     * Creates an instance of this command, the <code>CommitCommand</code> is used
+     * to ensure that all used <code>ResourceProcessor</code>s will be committed at a later
+     * stage in the deployment session.
+     *
+     * @param commitCommand The <code>CommitCommand</code> that will commit all resource processors used in this command.
+     */
+    public ProcessResourceCommand(CommitResourceCommand commitCommand) {
+        m_commitCommand = commitCommand;
+        addRollback(m_commitCommand);
+    }
+
+    public void execute(DeploymentSessionImpl session) throws DeploymentException {
+        AbstractDeploymentPackage source = session.getSourceAbstractDeploymentPackage();
+        BundleContext context = session.getBundleContext();
+
+        Map expectedResources = new HashMap();
+        AbstractInfo[] resourceInfos = (AbstractInfo[]) source.getBundleInfos();
+        for (int i = 0; i < resourceInfos.length; i++) {
+            AbstractInfo resourceInfo = resourceInfos[i];
+            if(!resourceInfo.isMissing()) {
+                expectedResources.put(resourceInfo.getPath(), resourceInfo);
+            }
+        }
+
+        try {
+            for (AbstractInfo jarEntry = source.getNextEntry(); (jarEntry != null) && (!expectedResources.isEmpty()); jarEntry = source.getNextEntry()) {
+                String name = jarEntry.getPath();
+
+                if (!expectedResources.containsKey(name)) {
+                    throw new DeploymentException(DeploymentException.CODE_OTHER_ERROR, "Resource '" + name + "' is not described in the manifest.");
+                }
+
+                ResourceInfoImpl resourceInfo = (ResourceInfoImpl) expectedResources.remove(name);
+
+                String resourceProcessorPID = resourceInfo.getResourceProcessor();
+                if (resourceProcessorPID == null) {
+                    // no resource processor specified, resource can be ignored
+                    continue;
+                }
+
+                ServiceReference ref = source.getResourceProcessor(resourceProcessorPID);
+                if (ref != null) {
+                    String serviceOwnerSymName = ref.getBundle().getSymbolicName();
+                    if (source.getBundleInfoByName(serviceOwnerSymName) != null) {
+                        ResourceProcessor resourceProcessor = (ResourceProcessor) context.getService(ref);
+                        if (resourceProcessor != null) {
+                            resourceProcessor.begin(session);
+                            try {
+                                m_commitCommand.addResourceProcessor(resourceProcessor);
+                                resourceProcessor.process(name, source.getCurrentEntryStream());
+                            }
+                            catch (ResourceProcessorException rpe) {
+                                if (rpe.getCode() == ResourceProcessorException.CODE_RESOURCE_SHARING_VIOLATION) {
+                                    throw new DeploymentException(DeploymentException.CODE_RESOURCE_SHARING_VIOLATION, "Violation while processing resource '" + name + "'", rpe);
+                                }
+                                else {
+                                    throw new DeploymentException(DeploymentException.CODE_OTHER_ERROR, "Error while processing resource '" + name + "'", rpe);
+                                }
+                            }
+                        }
+                        else {
+                            throw new DeploymentException(DeploymentException.CODE_PROCESSOR_NOT_FOUND, "No resource processor for resource: '" + name + "'");
+                        }
+                    }
+                    else {
+                        throw new DeploymentException(DeploymentException.CODE_FOREIGN_CUSTOMIZER, "Resource processor for resource '" + name + "' belongs to foreign deployment package");
+                    }
+                }
+                else {
+                    throw new DeploymentException(DeploymentException.CODE_PROCESSOR_NOT_FOUND, "No resource processor for resource: '" + name + "'");
+                }
+            }
+        }
+        catch (IOException e) {
+            throw new DeploymentException(DeploymentException.CODE_OTHER_ERROR, "Problem while reading stream", e);
+        }
+    }
+
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/SnapshotCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/SnapshotCommand.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/SnapshotCommand.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/SnapshotCommand.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,224 @@
+/*
+ * 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.felix.deploymentadmin.spi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.felix.deploymentadmin.AbstractDeploymentPackage;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.deploymentadmin.BundleInfo;
+import org.osgi.service.deploymentadmin.DeploymentException;
+import org.osgi.service.log.LogService;
+
+public class SnapshotCommand extends Command {
+
+    private final GetStorageAreaCommand m_getStorageAreaCommand;
+
+    public SnapshotCommand(GetStorageAreaCommand getStorageAreaCommand) {
+        m_getStorageAreaCommand = getStorageAreaCommand;
+    }
+
+    public void execute(DeploymentSessionImpl session) throws DeploymentException {
+        AbstractDeploymentPackage target = session.getTargetAbstractDeploymentPackage();
+        BundleContext context = session.getBundleContext();
+
+        BundleInfo[] infos = target.getBundleInfos();
+        Map storageAreas = m_getStorageAreaCommand.getStorageAreas();
+        for (int i = 0; i < infos.length; i++) {
+            if (isCancelled()) {
+                throw new DeploymentException(DeploymentException.CODE_CANCELLED);
+            }
+            Bundle bundle = target.getBundle(infos[i].getSymbolicName());
+            if (bundle != null) {
+                File root = (File) storageAreas.get(bundle.getSymbolicName());
+                if (root != null) {
+                    File snapshot = context.getDataFile("snapshots");
+                    snapshot.mkdirs();
+                    snapshot = new File(snapshot, infos[i].getSymbolicName());
+                    try {
+                        snapshot.createNewFile();
+                        store(root, snapshot);
+                        addRollback(new RestoreSnapshotRunnable(snapshot, root));
+                        addCommit(new DeleteSnapshotRunnable(snapshot));
+                    }
+                    catch (IOException e) {
+                        snapshot.delete();
+                    }
+                } else {
+                    session.getLog().log(LogService.LOG_WARNING, "Could not retrieve storage area of bundle '" + bundle.getSymbolicName() + "', skipping it.");
+                }
+            }
+        }
+    }
+
+    private void delete(File root, boolean deleteRoot) {
+        if (root.isDirectory()) {
+            File[] childs = root.listFiles();
+            for (int i = 0; i < childs.length; i++) {
+                delete(childs[i], true);
+            }
+        }
+        if (deleteRoot) {
+            root.delete();
+        }
+    }
+
+    private void store(File source, File target) throws IOException {
+        ZipOutputStream output = null;
+        try {
+            File[] children = source.listFiles();
+            output = new ZipOutputStream(new FileOutputStream(target));
+            for (int i = 0; i < children.length; i++) {
+                storeRecursive(target, new File(children[i].getName()), output);
+            }
+        }
+        finally {
+            if (output != null) {
+                try {
+                    output.close();
+                }
+                catch (Exception ex) {
+                    // Not much we can do
+                }
+            }
+        }
+    }
+
+    private void storeRecursive(File current, File path, ZipOutputStream output) throws IOException {
+        output.putNextEntry(new ZipEntry(path.getPath()));
+        if (current.isDirectory()) {
+            output.closeEntry();
+            File[] childs = current.listFiles();
+            for (int i = 0; i < childs.length; i++) {
+                storeRecursive(childs[i], new File(path, childs[i].getName()), output);
+            }
+        }
+        else {
+            InputStream input = null;
+            try {
+                input = new FileInputStream(current);
+                byte[] buffer = new byte[4096];
+                for (int i = input.read(buffer); i != -1; i = input.read(buffer)) {
+                    output.write(buffer, 0, i);
+                }
+                output.closeEntry();
+            }
+            finally {
+                try {
+                    if (input != null) {
+                        input.close();
+                    }
+                }
+                catch (Exception ex) {
+                    // Not much we can do
+                }
+            }
+        }
+    }
+
+    private void unpack(File source, File target) throws IOException {
+        ZipInputStream input = null;
+        try {
+            input = new ZipInputStream(new FileInputStream(source));
+            for (ZipEntry entry = input.getNextEntry(); entry != null; entry = input.getNextEntry()) {
+                if (entry.isDirectory()) {
+                    (new File(target, entry.getName())).mkdirs();
+                }
+                else {
+                    OutputStream output = null;
+                    try {
+                        output = new FileOutputStream(target);
+                        byte[] buffer = new byte[4096];
+                        for (int i = input.read(buffer); i > -1; i = input.read(buffer)) {
+                            output.write(buffer, 0, i);
+                        }
+                    }
+                    finally {
+                        if (output != null) {
+                            try {
+                                output.close();
+                            }
+                            catch (Exception ex) {
+                                // Not much we can do
+                            }
+                        }
+                    }
+                }
+                input.closeEntry();
+            }
+        }
+        finally {
+            if (input != null) {
+                try {
+                    input.close();
+                }
+                catch (Exception ex) {
+                    // Not much we can do
+                }
+            }
+        }
+    }
+
+    class DeleteSnapshotRunnable implements Runnable {
+
+        private final File m_snapshot;
+
+        private DeleteSnapshotRunnable(File snapshot) {
+            m_snapshot = snapshot;
+        }
+
+        public void run() {
+            m_snapshot.delete();
+        }
+    }
+
+    private class RestoreSnapshotRunnable implements Runnable {
+
+        private final File m_snapshot;
+        private final File m_root;
+
+        private RestoreSnapshotRunnable(File snapshot, File root) {
+            m_snapshot = snapshot;
+            m_root = root;
+        }
+
+        public void run() {
+            try {
+                delete(m_root, false);
+                unpack(m_snapshot, m_root);
+            }
+            catch (Exception ex) {
+                // TODO: log this
+            }
+            finally {
+                m_snapshot.delete();
+            }
+        }
+    }
+}
\ No newline at end of file

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/StartBundleCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/StartBundleCommand.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/StartBundleCommand.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/StartBundleCommand.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,129 @@
+/*
+ * 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.felix.deploymentadmin.spi;
+
+import org.apache.felix.deploymentadmin.AbstractDeploymentPackage;
+import org.apache.felix.deploymentadmin.BundleInfoImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.service.log.LogService;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * Command that starts all bundles described in the source deployment package of a deployment session.
+ */
+public class StartBundleCommand extends Command {
+
+    private final RefreshPackagesMonitor m_refreshMonitor = new RefreshPackagesMonitor();
+    private static final int REFRESH_TIMEOUT = 10000;
+
+    public void execute(DeploymentSessionImpl session) {
+        AbstractDeploymentPackage source = session.getSourceAbstractDeploymentPackage();
+        BundleContext context = session.getBundleContext();
+        PackageAdmin packageAdmin = session.getPackageAdmin();
+        RefreshPackagesListener listener = new RefreshPackagesListener();
+        LogService log = session.getLog();
+
+        context.addFrameworkListener(listener);
+        packageAdmin.refreshPackages(null);
+        m_refreshMonitor.waitForRefresh();
+        context.removeFrameworkListener(listener);
+
+        // start source bundles
+        BundleInfoImpl[] bundleInfos = source.getOrderedBundleInfos();
+        for (int i = 0; i < bundleInfos.length; i++) {
+            BundleInfoImpl bundleInfoImpl = bundleInfos[i];
+            if(!bundleInfoImpl.isCustomizer()) {
+                Bundle bundle = source.getBundle(bundleInfoImpl.getSymbolicName());
+                if (bundle != null) {
+                    try {
+                        bundle.start();
+                    }
+                    catch (BundleException be) {
+                        log.log(LogService.LOG_WARNING, "Could not start bundle '" + bundle.getSymbolicName() + "'", be);
+                    }
+                }
+                else {
+                	log.log(LogService.LOG_WARNING, "Could not start bundle '" + bundleInfoImpl.getSymbolicName() + "' because it is no defined in the framework");
+                }
+            }
+        }
+    }
+
+    /**
+     * RefreshPackagesListener is only listing to FrameworkEvents of the type PACKAGES_REFRESHED. It will
+     * notify any object waiting the completion of a refreshpackages() call.
+     */
+    private class RefreshPackagesListener implements FrameworkListener {
+        public void frameworkEvent(FrameworkEvent event) {
+            if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
+                // TODO: m_log.log(LogService.LOG_INFO, "Packages refreshed event received");
+                m_refreshMonitor.proceed();
+            }
+        }
+    }
+
+    /**
+     * Use this monitor when its desired to wait for the completion of the asynchronous PackageAdmin.refreshPackages() call.
+     */
+    private class RefreshPackagesMonitor {
+        private boolean m_alreadyNotified = false;
+
+        /**
+         * Waits for the completion of the PackageAdmin.refreshPackages() call. Because
+         * its not sure whether all OSGi framework implementations implement this method as
+         * specified we have build in a timeout. So if a event about the completion of the
+         * refreshpackages() is never received, we continue after the timeout whether the refresh
+         * was done or not.
+         */
+        public synchronized void waitForRefresh() {
+            if (!m_alreadyNotified) {
+             // TODO: m_log.log(LogService.LOG_DEBUG, "wait for Packages refreshed event");
+                try {
+                    wait(REFRESH_TIMEOUT);
+                }
+                catch (InterruptedException ie) {
+                 // TODO: m_log.log(LogService.LOG_INFO, "interrupted while waiting for packages refreshed event", ie);
+                }
+                finally {
+                    // just reset the misted notification variable, this Monitor object might be reused.
+                    m_alreadyNotified = false;
+                }
+            }
+            else {
+                // TODO: m_log.log(LogService.LOG_DEBUG, "won't wait for Packages refreshed event, event is already received");
+                // just reset the misted notification variable, this Monitor object might be reused.
+                m_alreadyNotified = false;
+            }
+        }
+
+        /**
+         * After a PACKAGES_REFRESHED event notify all the parties interested in the completion of
+         * the PackageAdmin.refreshPackages() call.
+         */
+        public synchronized void proceed() {
+            m_alreadyNotified = true;
+            notifyAll();
+        }
+    }
+
+}

Added: felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/StartCustomizerCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/StartCustomizerCommand.java?rev=616813&view=auto
==============================================================================
--- felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/StartCustomizerCommand.java (added)
+++ felix/trunk/deploymentadmin/src/main/java/org/apache/felix/deploymentadmin/spi/StartCustomizerCommand.java Wed Jan 30 08:46:24 2008
@@ -0,0 +1,93 @@
+/*
+ * 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.felix.deploymentadmin.spi;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.felix.deploymentadmin.AbstractDeploymentPackage;
+import org.apache.felix.deploymentadmin.BundleInfoImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.service.deploymentadmin.DeploymentException;
+
+/**
+ * Command that starts all customizer bundles defined in the source deployment packages of a deployment
+ * session. In addition all customizer bundles of the target deployment package that are not present in the source
+ * deployment package are started as well.
+ */
+public class StartCustomizerCommand extends Command {
+
+    public void execute(DeploymentSessionImpl session) throws DeploymentException {
+        AbstractDeploymentPackage target = session.getTargetAbstractDeploymentPackage();
+        AbstractDeploymentPackage source = session.getSourceAbstractDeploymentPackage();
+        Set bundles = new HashSet();
+        Set sourceBundlePaths = new HashSet();
+        BundleInfoImpl[] targetInfos = target.getBundleInfoImpls();
+        BundleInfoImpl[] sourceInfos = source.getBundleInfoImpls();
+        for(int i = 0; i < sourceInfos.length; i++) {
+            if (sourceInfos[i].isCustomizer()) {
+                sourceBundlePaths.add(sourceInfos[i].getPath());
+                Bundle bundle = source.getBundle(sourceInfos[i].getSymbolicName());
+                if (bundle != null) {
+                    bundles.add(bundle);
+                }
+            }
+        }
+        for(int i = 0; i < targetInfos.length; i++) {
+            if (targetInfos[i].isCustomizer() && !sourceBundlePaths.contains(targetInfos[i].getPath())) {
+                Bundle bundle = target.getBundle(targetInfos[i].getSymbolicName());
+                if (bundle != null) {
+                    bundles.add(bundle);
+                }
+            }
+        }
+        for(Iterator i = bundles.iterator(); i.hasNext(); ) {
+            Bundle bundle = (Bundle) i.next();
+            try {
+                bundle.start();
+            }
+            catch (BundleException be) {
+                throw new DeploymentException(DeploymentException.CODE_OTHER_ERROR, "Could not start customizer bundle '" + bundle.getSymbolicName() + "'", be);
+            }
+            addRollback(new StopCustomizerRunnable(bundle));
+        }
+    }
+
+    private static class StopCustomizerRunnable implements Runnable {
+
+        private final Bundle m_bundle;
+
+        public StopCustomizerRunnable(Bundle bundle) {
+            m_bundle = bundle;
+        }
+
+        public void run() {
+            try {
+                m_bundle.stop();
+            }
+            catch (BundleException e) {
+                // TODO log this
+                e.printStackTrace();
+            }
+        }
+
+    }
+}