FELIX-2115: The api offers no way to have a timeout or cancel the resolution if it takes too long

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@912549 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ResolverImpl.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ResolverImpl.java
index 5f6dd3d..32b6318 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ResolverImpl.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ResolverImpl.java
@@ -301,6 +301,7 @@
     {
         for (Iterator iter = m_addedSet.iterator(); iter.hasNext(); )
         {
+            checkInterrupt();
             Resource resource = (Resource) iter.next();
             Capability[] caps = resource.getCapabilities();
             for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
@@ -322,6 +323,7 @@
     {
         for (Iterator iterator = m_resolveSet.iterator(); iterator.hasNext(); )
         {
+            checkInterrupt();
             Resource resource = (Resource) iterator.next();
             Capability[] caps = resource.getCapabilities();
             for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
@@ -348,6 +350,7 @@
         Resource[] resources = m_local.getResources();
         for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
         {
+            checkInterrupt();
             // We don't need to look at resources we've already looked at.
             if (!m_failedSet.contains(resources[resIdx])
                 && !m_resolveSet.contains(resources[resIdx]))
@@ -382,6 +385,7 @@
             Resource[] resources = repos[repoIdx].getResources();
             for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
             {
+                checkInterrupt();
                 // We don't need to look at resources we've already looked at.
                 if (!m_failedSet.contains(resources[resIdx])
                     && !m_resolveSet.contains(resources[resIdx]))
@@ -473,6 +477,14 @@
         return (best == null) ? null : best;
     }
 
+    private void checkInterrupt()
+    {
+        if (Thread.interrupted())
+        {
+            throw new InterrupteResolutionException();
+        }
+    }
+
     public synchronized void deploy(boolean start)
     {
         // Must resolve if not already resolved.
diff --git a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/ResolverImplTest.java b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/ResolverImplTest.java
index 6a14a04..5f71856 100644
--- a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/ResolverImplTest.java
+++ b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/ResolverImplTest.java
@@ -22,6 +22,7 @@
 
 import junit.framework.TestCase;
 
+import org.osgi.service.obr.InterrupteResolutionException;
 import org.osgi.service.obr.Repository;
 import org.osgi.service.obr.Requirement;
 import org.osgi.service.obr.Resolver;
@@ -76,6 +77,26 @@
         assertTrue(resolver.resolve());
     }
 
+    public void testResolveInterrupt() throws Exception
+    {
+        RepositoryAdminImpl repoAdmin = createRepositoryAdmin();
+        repoAdmin.addRepository(getClass().getResource("/repo_for_resolvertest.xml"));
+
+        Resolver resolver = repoAdmin.resolver();
+        resolver.add(repoAdmin.requirement("package", "(package=org.apache.felix.test.osgi)"));
+
+        Thread.currentThread().interrupt();
+        try
+        {
+            resolver.resolve();
+            fail("An excepiton should have been thrown");
+        }
+        catch (InterrupteResolutionException e)
+        {
+            // ok
+        }
+    }
+
     public static void main(String[] args) throws Exception
     {
         new ResolverImplTest().testReferral1();
diff --git a/org.osgi.service.obr/src/main/java/org/osgi/service/obr/InterrupteResolutionException.java b/org.osgi.service.obr/src/main/java/org/osgi/service/obr/InterrupteResolutionException.java
new file mode 100644
index 0000000..c12b3c5
--- /dev/null
+++ b/org.osgi.service.obr/src/main/java/org/osgi/service/obr/InterrupteResolutionException.java
@@ -0,0 +1,48 @@
+/*
+ * 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.osgi.service.obr;
+
+/**
+ *
+ * Exception thrown by the resolver if the resolution has been interrupted.
+ */
+public class InterrupteResolutionException extends RuntimeException
+{
+
+    public InterrupteResolutionException()
+    {
+    }
+
+    public InterrupteResolutionException(String message)
+    {
+        super(message);
+    }
+
+    public InterrupteResolutionException(String message, Throwable cause)
+    {
+        super(message, cause);
+    }
+
+    public InterrupteResolutionException(Throwable cause)
+    {
+        super(cause);
+    }
+
+}
+
diff --git a/org.osgi.service.obr/src/main/java/org/osgi/service/obr/Resolver.java b/org.osgi.service.obr/src/main/java/org/osgi/service/obr/Resolver.java
index a8a552d..fd096a4 100644
--- a/org.osgi.service.obr/src/main/java/org/osgi/service/obr/Resolver.java
+++ b/org.osgi.service.obr/src/main/java/org/osgi/service/obr/Resolver.java
@@ -70,6 +70,15 @@
 
     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 InterrupteResolutionException if the resolution has been interrupted
+     */
     boolean resolve();
 
     void deploy(boolean start);