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 071c13d5d3..c17aec071b 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 @@ -533,6 +533,8 @@ public synchronized void deploy(int flags) // resources being deployed. if ((localResource != null) && isResourceUpdatable(localResource, deployResource, deployResources)) { + // Track if we need to start the bundle + boolean doStartBundle = (flags & START) != 0; // Only update if it is a different version. if (!localResource.equals(deployResource)) { // Update the installed bundle. @@ -540,7 +542,6 @@ public synchronized void deploy(int flags) // stop the bundle before updating to prevent // the bundle update from throwing due to not yet // resolved dependencies - boolean doStartBundle = (flags & START) != 0; if (localResource.getBundle().getState() == Bundle.ACTIVE) { doStartBundle = true; localResource.getBundle().stop(); @@ -552,9 +553,7 @@ public synchronized void deploy(int flags) // started later. if (doStartBundle) { Bundle bundle = localResource.getBundle(); - if (!isFragmentBundle(bundle)) { - startList.add(bundle); - } + startList.add(bundle); } } catch (Exception ex) { m_logger.log( @@ -564,6 +563,13 @@ public synchronized void deploy(int flags) return; } } + else if (doStartBundle) { + // If necessary, save the updated bundle to be started later. + int state = localResource.getBundle().getState(); + if (state == Bundle.INSTALLED || state == Bundle.RESOLVED) { + startList.add(localResource.getBundle()); + } + } } else { // Install the bundle. try { @@ -581,9 +587,7 @@ public synchronized void deploy(int flags) // If necessary, save the installed bundle to be // started later. if ((flags & START) != 0) { - if (!isFragmentBundle(bundle)) { - startList.add(bundle); - } + startList.add(bundle); } } catch (Exception ex) { m_logger.log( @@ -597,7 +601,9 @@ public synchronized void deploy(int flags) for (Bundle aStartList : startList) { try { - aStartList.start(); + if (!isFragmentBundle(aStartList)) { + aStartList.start(); + } } catch (BundleException ex) { m_logger.log( Logger.LOG_ERROR, diff --git a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/ResolverImplTest.java b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/ResolverImplTest.java index 867010768d..61e5f86b32 100644 --- a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/ResolverImplTest.java +++ b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/ResolverImplTest.java @@ -130,6 +130,8 @@ public void testSpec2() throws Exception resolver.add(discoverResources[0]); assertTrue("Resolver could not resolve", resolver.resolve()); + + EasyMock.verify(resource, resource2); } public void testSpecBundleNamespace() throws Exception @@ -224,10 +226,86 @@ public void testMandatoryPackages() throws Exception } public void testFindUpdatableLocalResource() throws Exception { + Bundle mockBundle = EasyMock.createMock(Bundle.class); + EasyMock.expect(mockBundle.getState()).andReturn(Bundle.ACTIVE).anyTimes(); + EasyMock.expect(mockBundle.getHeaders()).andReturn(new Hashtable<>()).anyTimes(); + + mockBundle.stop(); + EasyMock.expectLastCall().once(); + mockBundle.update(EasyMock.anyObject()); + EasyMock.expectLastCall().once(); + mockBundle.start(); + EasyMock.expectLastCall().once(); + + // In the implementation of ResolverImpl the static method FileUtil.openURL is used. + // EasyMock doesn't have static method testing, Mockito doesn't have it in the currently used version. + // So for now reference a local file which we know exists to be loaded. + String uri = getClass().getResource("ResolverImplTest.class").toURI().toString(); + LocalResource resource = EasyMock.createMock(LocalResource.class); EasyMock.expect(resource.getSymbolicName()).andReturn("com.test.bundleA").anyTimes(); EasyMock.expect(resource.getRequirements()).andReturn(null).anyTimes(); EasyMock.expect(resource.getCapabilities()).andReturn(null).anyTimes(); + EasyMock.expect(resource.getBundle()).andReturn(mockBundle).anyTimes(); + EasyMock.expect(resource.getURI()).andReturn(uri).anyTimes(); + EasyMock.expect(resource.isLocal()).andReturn(true).anyTimes(); + + // Ensure the resource to deploy is different that the local resource, thus triggering an update + LocalResource resourceToDeploy = EasyMock.createMock(LocalResource.class); + EasyMock.expect(resourceToDeploy.getSymbolicName()).andReturn("com.test.bundleA").anyTimes(); + EasyMock.expect(resourceToDeploy.getRequirements()).andReturn(null).anyTimes(); + EasyMock.expect(resourceToDeploy.getCapabilities()).andReturn(null).anyTimes(); + EasyMock.expect(resourceToDeploy.getBundle()).andReturn(mockBundle).anyTimes(); + EasyMock.expect(resourceToDeploy.getURI()).andReturn(uri).anyTimes(); + EasyMock.expect(resourceToDeploy.isLocal()).andReturn(true).anyTimes(); + + Repository localRepo = EasyMock.createMock(Repository.class); + + Repository[] localRepos = { localRepo }; + final LocalResource[] localResources = { resource }; + + EasyMock.expect(localRepo.getResources()).andReturn(localResources).anyTimes(); + EasyMock.expect(localRepo.getURI()).andReturn(Repository.LOCAL).anyTimes(); + EasyMock.expect(localRepo.getLastModified()).andReturn(System.currentTimeMillis()).anyTimes(); + + BundleContext bundleContext = EasyMock.createMock(BundleContext.class); + + EasyMock.replay(resource, resourceToDeploy, mockBundle, localRepo); + + ResolverImpl resolver = new ResolverImpl(bundleContext, localRepos, new Logger(bundleContext)) { + @Override + public LocalResource[] getLocalResources() { + return localResources; + } + }; + + resolver.add(resourceToDeploy); + + boolean exceptionThrown = false; + try { + resolver.resolve(); + resolver.deploy(Resolver.START); + } catch (Exception e) { + e.printStackTrace(); + exceptionThrown = true; + } + assertFalse(exceptionThrown); + + EasyMock.verify(resource, resourceToDeploy, mockBundle, localRepo); + } + + public void testDeployFragmentBundle() throws Exception { + Bundle mockBundle = EasyMock.createMock(Bundle.class); + EasyMock.expect(mockBundle.getState()).andReturn(Bundle.ACTIVE).anyTimes(); + Hashtable bundleHeaders = new Hashtable<>(); + bundleHeaders.put(Constants.FRAGMENT_HOST, "com.test.bundleA"); + EasyMock.expect(mockBundle.getHeaders()).andReturn(bundleHeaders).anyTimes(); + + LocalResource resource = EasyMock.createMock(LocalResource.class); + EasyMock.expect(resource.getSymbolicName()).andReturn("com.test.bundleA").anyTimes(); + EasyMock.expect(resource.getRequirements()).andReturn(null).anyTimes(); + EasyMock.expect(resource.getCapabilities()).andReturn(null).anyTimes(); + EasyMock.expect(resource.getBundle()).andReturn(mockBundle).anyTimes(); EasyMock.expect(resource.getURI()).andReturn("http://test.com").anyTimes(); EasyMock.expect(resource.isLocal()).andReturn(true).anyTimes(); @@ -242,7 +320,7 @@ public void testFindUpdatableLocalResource() throws Exception { BundleContext bundleContext = EasyMock.createMock(BundleContext.class); - EasyMock.replay(resource, localRepo); + EasyMock.replay(resource, mockBundle, localRepo); ResolverImpl resolver = new ResolverImpl(bundleContext, localRepos, new Logger(bundleContext)) { @Override @@ -262,6 +340,8 @@ public LocalResource[] getLocalResources() { exceptionThrown = true; } assertFalse(exceptionThrown); + + EasyMock.verify(resource, mockBundle, localRepo); } public static void main(String[] args) throws Exception