FELIX-1492: add a flag to exclude optional resources in both resolution and deployment

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@917433 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/RepositoryAdmin.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/RepositoryAdmin.java
index f346be9..8a9d9bc 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/RepositoryAdmin.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/RepositoryAdmin.java
@@ -22,6 +22,7 @@
 package org.apache.felix.bundlerepository;
 
 import java.net.URL;
+import java.util.Map;
 
 import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
@@ -164,4 +165,13 @@
      */
     Repository repository(URL repository) throws Exception;
 
+    /**
+     * Create a capability
+     *
+     * @param name name of this capability
+     * @param properties the properties
+     * @return
+     */
+    Capability capability(String name, Map properties);
+
 }
\ No newline at end of file
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/Resolver.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/Resolver.java
index 2f0cae8..3976832 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/Resolver.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/Resolver.java
@@ -24,6 +24,12 @@
 public interface Resolver
 {
 
+    int NO_OPTIONAL_RESOURCES =    0x0001;
+    int NO_LOCAL_RESOURCES =       0x0002;
+    int NO_SYSTEM_BUNDLE =         0x0004;
+    int DO_NOT_PREFER_LOCAL =      0x0008;
+    int START =                    0x0010;
+
     /**
      * Add the following resource to the resolution.
      *
@@ -60,6 +66,25 @@
      */
     Requirement[] getAddedRequirements();
 
+   /**
+     * Start the resolution process and return whether the constraints have
+     * been successfully met or not.
+     * The resolution can be interrupted by a call to Thread.interrupt() at any
+     * time.  The result will be to stop the resolver and throw an InterruptedException.
+     *
+     * @return <code>true</code> if the resolution has succeeded else <code>false</code>
+     * @throws InterruptedResolutionException if the resolution has been interrupted
+     */
+    boolean resolve() throws InterruptedResolutionException;
+
+    /**
+     * Start the resolution process with the following flags.
+     * @param flags resolution flags
+     * @return <code>true</code> if the resolution has succeeded else <code>false</code>
+     * @throws InterruptedResolutionException if the resolution has been interrupted
+     */
+    boolean resolve(int flags) throws InterruptedResolutionException;
+
     Requirement[] getUnsatisfiedRequirements();
 
     Resource[] getOptionalResources();
@@ -70,16 +95,7 @@
 
     Resource[] getRequiredResources();
 
-    /**
-     * Start the resolution process and return whether the constraints have
-     * been successfully met or not.
-     * The resolution can be interrupted by a call to Thread.interrupt() at any
-     * time.  The result will be to stop the resolver and throw an InterruptedException.
-     *
-     * @return <code>true</code> if the resolution has succeeded else <code>false</code>
-     * @throws InterruptedResolutionException if the resolution has been interrupted
-     */
-    boolean resolve();
-
     void deploy(boolean start);
+
+    void deploy(int flags);
 }
\ No newline at end of file
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/RepositoryAdminImpl.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/RepositoryAdminImpl.java
index 5a1db98..9e59ada 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/RepositoryAdminImpl.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/RepositoryAdminImpl.java
@@ -24,6 +24,7 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
@@ -252,6 +253,18 @@
         return new RepositoryImpl(null, url, 0, m_logger);
     }
 
+    public Capability capability(String name, Map properties)
+    {
+        CapabilityImpl cap = new CapabilityImpl();
+        cap.setName(name);
+        for (Iterator it = properties.entrySet().iterator(); it.hasNext();)
+        {
+            Map.Entry e = (Map.Entry) it.next();
+            cap.addP((String) e.getKey(), e.getValue());
+        }
+        return cap;
+    }
+
     private void initialize()
     {
         m_initialized = true;
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/ResolverImpl.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/ResolverImpl.java
index b5bc169..07993ef 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/ResolverImpl.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/ResolverImpl.java
@@ -27,8 +27,6 @@
 
 public class ResolverImpl implements Resolver
 {
-    public static final String PREFER_LOCAL = "obr.resolver.preferLocal";
-
     private final BundleContext m_context;
     private final Logger m_logger;
     private final Repository[] m_repositories;
@@ -42,18 +40,14 @@
     private final Map m_unsatisfiedMap = new HashMap();
     private boolean m_resolved = false;
     private long m_resolveTimeStamp;
-    private boolean m_preferLocal = true;
+    private int m_resolutionFlags;
+    private int m_deployFlags;
 
     public ResolverImpl(BundleContext context, Repository[] repositories, Logger logger)
     {
         m_context = context;
         m_logger = logger;
         m_repositories = repositories;
-        String s = context.getProperty(PREFER_LOCAL);
-        if (s != null)
-        {
-            m_preferLocal = Boolean.parseBoolean(s);
-        }
     }
 
     public synchronized void add(Resource resource)
@@ -136,6 +130,14 @@
         {
             if (m_repositories[repoIdx].isLocal() == local)
             {
+                boolean isLocal = m_repositories[repoIdx] instanceof LocalRepositoryImpl;
+                boolean isSystem = m_repositories[repoIdx] instanceof SystemRepositoryImpl;
+                if (isLocal && (m_resolutionFlags & NO_LOCAL_RESOURCES) != 0) {
+                    continue;
+                }
+                if (isSystem && (m_resolutionFlags & NO_SYSTEM_BUNDLE) != 0) {
+                    continue;
+                }
                 resources.addAll(Arrays.asList(m_repositories[repoIdx].getResources()));
             }
         }
@@ -144,6 +146,11 @@
 
     public synchronized boolean resolve()
     {
+        return resolve(0);
+    }
+
+    public synchronized boolean resolve(int flags)
+    {
         // Find resources
         Resource[] locals = getResources(true);
         Resource[] remotes = getResources(false);
@@ -166,6 +173,7 @@
         m_reasonMap.clear();
         m_unsatisfiedMap.clear();
         m_resolved = true;
+        m_resolutionFlags = flags;
 
         boolean result = true;
 
@@ -230,9 +238,14 @@
         Requirement[] reqs = resource.getRequirements();
         if (reqs != null)
         {
-            Resource candidate = null;
+            Resource candidate;
             for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
             {
+                // Do not resolve optional requirements
+                if ((m_resolutionFlags & NO_OPTIONAL_RESOURCES) != 0 && reqs[reqIdx].isOptional())
+                {
+                    continue;
+                }
                 candidate = searchResources(reqs[reqIdx], m_addedSet);
                 if (candidate == null)
                 {
@@ -258,9 +271,9 @@
                         Capability bestCapability = getBestCandidate(candidateCapabilities);
 
                         // Try to resolve the best resource.
-                        if (resolve(((CapabilityImpl) bestCapability).getResource(), locals, remotes, optional || reqs[reqIdx].isOptional()))
+                        if (resolve(bestCapability.getResource(), locals, remotes, optional || reqs[reqIdx].isOptional()))
                         {
-                            candidate = ((CapabilityImpl) bestCapability).getResource();
+                            candidate = bestCapability.getResource();
                         }
                         else
                         {
@@ -354,7 +367,8 @@
 
     /**
      * Searches for resources that do meet the given requirement
-     * @param req
+     * @param req the the requirement that must be satisfied by resources
+     * @param resources list of resources to look at
      * @return all resources meeting the given requirement
      */
     private List searchResources(Requirement req, Resource[] resources)
@@ -398,7 +412,7 @@
         for(int capIdx = 0; capIdx < caps.size(); capIdx++)
         {
             Capability current = (Capability) caps.get(capIdx);
-            boolean isCurrentLocal = ((CapabilityImpl) current).getResource().getRepository() == null;
+            boolean isCurrentLocal = current.getResource().getRepository() == null;
 
             if (best == null)
             {
@@ -410,15 +424,15 @@
                     bestVersion = (Version) v;
                 }
             }
-            else if (!m_preferLocal || !bestLocal || isCurrentLocal)
+            else if ((m_resolutionFlags & DO_NOT_PREFER_LOCAL) != 0 || !bestLocal || isCurrentLocal)
             {
                 Object v = current.getProperties().get(Resource.VERSION);
 
                 // If there is no version, then select the resource
                 // with the greatest number of capabilities.
                 if ((v == null) && (bestVersion == null)
-                    && (((CapabilityImpl) best).getResource().getCapabilities().length
-                        < ((CapabilityImpl) current).getResource().getCapabilities().length))
+                    && (best.getResource().getCapabilities().length
+                        < current.getResource().getCapabilities().length))
                 {
                     best = current;
                     bestLocal = isCurrentLocal;
@@ -438,8 +452,8 @@
                     // best, then select the one with the greatest
                     // number of capabilities.
                     else if ((bestVersion != null) && (bestVersion.compareTo(v) == 0)
-                            && (((CapabilityImpl) best).getResource().getCapabilities().length
-                                < ((CapabilityImpl) current).getResource().getCapabilities().length))
+                            && (best.getResource().getCapabilities().length
+                                < current.getResource().getCapabilities().length))
                     {
                         best = current;
                         bestLocal = isCurrentLocal;
@@ -462,8 +476,14 @@
 
     public synchronized void deploy(boolean start)
     {
+        deploy(START);
+    }
+
+    public synchronized void deploy(int flags)
+    {
+        m_deployFlags = flags;
         // Must resolve if not already resolved.
-        if (!m_resolved && !resolve())
+        if (!m_resolved && !resolve(flags))
         {
             m_logger.log(Logger.LOG_ERROR, "Resolver: Cannot resolve target resources.");
             return;
@@ -495,10 +515,13 @@
         {
             deployMap.put(resources[i], resources[i]);
         }
-        resources = getOptionalResources();
-        for (int i = 0; (resources != null) && (i < resources.length); i++)
+        if ((flags & NO_OPTIONAL_RESOURCES) == 0)
         {
-            deployMap.put(resources[i], resources[i]);
+            resources = getOptionalResources();
+            for (int i = 0; (resources != null) && (i < resources.length); i++)
+            {
+                deployMap.put(resources[i], resources[i]);
+            }
         }
         Resource[] deployResources = (Resource[])
             deployMap.keySet().toArray(new Resource[deployMap.size()]);
@@ -532,7 +555,7 @@
                         // stop the bundle before updating to prevent
                         // the bundle update from throwing due to not yet
                         // resolved dependencies
-                        boolean doStartBundle = start;
+                        boolean doStartBundle = (flags & START) != 0;
                         if (localResource.getBundle().getState() == Bundle.ACTIVE)
                         {
                             doStartBundle = true;
@@ -582,7 +605,7 @@
 
                         // If necessary, save the installed bundle to be
                         // started later.
-                        if (start)
+                        if ((flags & START) != 0)
                         {
                             if (!isFragmentBundle(bundle)) 
                             {