From 008c1c43aca0b8d10f54f6eb1bf55854128e1614 Mon Sep 17 00:00:00 2001 From: Uwe Barthel Date: Wed, 5 Mar 2014 07:34:52 +0100 Subject: [PATCH 1/2] [FELIX-4436] handle bundle if bundle location is a opaque uri --- .../internal/DirectoryWatcher.java | 18 +++- .../internal/DirectoryWatcherTest.java | 93 ++++++++++++++++++- 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java index 8d325266355..e71d1710367 100644 --- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java +++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java @@ -834,7 +834,23 @@ private void initializeCurrentManagedBundles() // Let's try to interpret the location as a file path uri = new File(location).toURI().normalize(); } - path = uri.getPath(); + if( uri.isOpaque() && uri.getSchemeSpecificPart() != null) + { + // blueprint:file:/tmp/foo/baa.jar -> file:/tmp/foo/baa.jar + // blueprint:mvn:foo.baa/baa/0.0.1 -> mvn:foo.baa/baa/0.0.1 + final String schemeSpecificPart = uri.getSchemeSpecificPart(); + // extract content behind the last colon of scheme specific path + final int offsetLastColon = schemeSpecificPart.lastIndexOf(':') + 1; + // file:/tmp/foo/baa.jar -> /tmp/foo/baa.jar + // mvn:foo.baa/baa/0.0.1 -> foo.baa/baa/0.0.1 + path = schemeSpecificPart.substring(offsetLastColon); + } + else + { + // file:/tmp/foo/baa.jar -> /tmp/foo/baa.jar + // mnv:foo.baa/baa/0.0.1 -> foo.baa/baa/0.0.1 + path = uri.getPath(); + } } if (path == null) { diff --git a/fileinstall/src/test/java/org/apache/felix/fileinstall/internal/DirectoryWatcherTest.java b/fileinstall/src/test/java/org/apache/felix/fileinstall/internal/DirectoryWatcherTest.java index 63fe969bb45..a3589a9b1f7 100644 --- a/fileinstall/src/test/java/org/apache/felix/fileinstall/internal/DirectoryWatcherTest.java +++ b/fileinstall/src/test/java/org/apache/felix/fileinstall/internal/DirectoryWatcherTest.java @@ -20,8 +20,10 @@ import java.io.File; +import java.net.URISyntaxException; import java.util.Dictionary; import java.util.Hashtable; +import java.util.Map; import junit.framework.TestCase; import org.easymock.EasyMock; @@ -51,7 +53,7 @@ protected void setUp() throws Exception super.setUp(); mockBundleContext = (BundleContext) EasyMock.createMock(BundleContext.class); mockPackageAdmin = (PackageAdmin) EasyMock.createMock(PackageAdmin.class); - mockBundle = (Bundle) EasyMock.createMock(Bundle.class); + mockBundle = (Bundle) EasyMock.createNiceMock(Bundle.class); props.put( DirectoryWatcher.DIR, new File( "target/load" ).getAbsolutePath() ); // Might get called, but most of the time it doesn't matter whether they do or don't. @@ -253,5 +255,94 @@ public void testInvalidTempDir() throws Exception } } + public void testNonOpaqueURIOnBundleLocation() throws URISyntaxException + { + final RuntimeException expectedException = new RuntimeException("expected exception to break execution on defined point."); + final File watchedDirectoryFile = new File("src/test/resources/watched"); + final String watchedDirectoryPath = watchedDirectoryFile.getAbsolutePath(); + + final String bundleFileName = "firstjar.jar"; + final File bundleFile = new File(watchedDirectoryPath,bundleFileName); + final String bundleLocation = "file:"+watchedDirectoryPath+'/'+bundleFileName; + + // break execution + Scanner scanner = new Scanner(watchedDirectoryFile) + { + public void initialize(Map checksums) + { + throw expectedException; + } + }; + + mockBundleContext.addBundleListener((BundleListener) org.easymock.EasyMock.anyObject()); + EasyMock.expect(mockBundleContext.getBundles()).andReturn(new Bundle[]{mockBundle}); + EasyMock.expect(mockBundleContext.getDataFile((String) EasyMock.anyObject())).andReturn(null).anyTimes(); + EasyMock.expect(mockBundle.getLocation()).andReturn(bundleLocation).anyTimes(); + Map mockCurrentManagedArtifacts = (Map)EasyMock.createNiceMock(Map.class); + EasyMock.expect(mockCurrentManagedArtifacts.put(EasyMock.eq(bundleFile), (Artifact)EasyMock.anyObject())).andReturn(null).times(1); + + EasyMock.replay(new Object[]{mockBundleContext, mockBundle, mockCurrentManagedArtifacts}); + + props.put(DirectoryWatcher.DIR, watchedDirectoryPath); + + dw = new DirectoryWatcher(props, mockBundleContext); + dw.noInitialDelay = true; + dw.currentManagedArtifacts = mockCurrentManagedArtifacts; + dw.scanner = scanner; + try { + dw.start(); + } + catch(RuntimeException e) + { + assertEquals(e, expectedException); + } + + EasyMock.verify(new Object[]{mockBundleContext, mockBundle, mockCurrentManagedArtifacts}); + } + + public void testOpaqueURIOnBundleLocation() throws URISyntaxException + { + final RuntimeException expectedException = new RuntimeException("expected exception to break execution on defined point."); + final File watchedDirectoryFile = new File("src/test/resources/watched"); + final String watchedDirectoryPath = watchedDirectoryFile.getAbsolutePath(); + + final String bundleFileName = "firstjar.jar"; + final File bundleFile = new File(watchedDirectoryPath,bundleFileName); + final String bundleLocation = "blueprint:file:"+watchedDirectoryPath+'/'+bundleFileName; + + // break execution + Scanner scanner = new Scanner(watchedDirectoryFile) + { + public void initialize(Map checksums) + { + throw expectedException; + } + }; + + mockBundleContext.addBundleListener((BundleListener) org.easymock.EasyMock.anyObject()); + EasyMock.expect(mockBundleContext.getBundles()).andReturn(new Bundle[]{mockBundle}); + EasyMock.expect(mockBundleContext.getDataFile((String) EasyMock.anyObject())).andReturn(null).anyTimes(); + EasyMock.expect(mockBundle.getLocation()).andReturn(bundleLocation).anyTimes(); + Map mockCurrentManagedArtifacts = (Map)EasyMock.createNiceMock(Map.class); + EasyMock.expect(mockCurrentManagedArtifacts.put(EasyMock.eq(bundleFile), (Artifact)EasyMock.anyObject())).andReturn(null).times(1); + + EasyMock.replay(new Object[]{mockBundleContext, mockBundle, mockCurrentManagedArtifacts}); + + props.put(DirectoryWatcher.DIR, watchedDirectoryPath); + + dw = new DirectoryWatcher(props, mockBundleContext); + dw.noInitialDelay = true; + dw.currentManagedArtifacts = mockCurrentManagedArtifacts; + dw.scanner = scanner; + try { + dw.start(); + } + catch(RuntimeException e) + { + assertEquals(e, expectedException); + } + + EasyMock.verify(new Object[]{mockBundleContext, mockBundle, mockCurrentManagedArtifacts}); + } } From b623ec0a2a607c4f8baa827660ba32bfdfccf09d Mon Sep 17 00:00:00 2001 From: Uwe Barthel Date: Fri, 14 Mar 2014 00:52:55 +0100 Subject: [PATCH 2/2] [FELIX-4436] handle bundle if bundle location is a opaque uri * use 'file:' instead of last index of colon for extract the path name from opaque uri * test added --- .../internal/DirectoryWatcher.java | 26 +++-- .../internal/DirectoryWatcherTest.java | 102 ++++++++++++++++-- 2 files changed, 108 insertions(+), 20 deletions(-) diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java index e71d1710367..a68ec7b172b 100644 --- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java +++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java @@ -43,7 +43,6 @@ import java.util.jar.JarInputStream; import java.util.jar.Manifest; import java.util.regex.Pattern; - import org.apache.felix.fileinstall.ArtifactInstaller; import org.apache.felix.fileinstall.ArtifactListener; import org.apache.felix.fileinstall.ArtifactTransformer; @@ -809,9 +808,9 @@ public void close() */ private void initializeCurrentManagedBundles() { - Bundle[] bundles = this.context.getBundles(); - String watchedDirPath = watchedDirectory.toURI().normalize().getPath(); - Map /**/ checksums = new HashMap/**/(); + final Bundle[] bundles = this.context.getBundles(); + final String watchedDirPath = watchedDirectory.toURI().normalize().getPath(); + final Map /**/ checksums = new HashMap/**/(); for (int i = 0; i < bundles.length; i++) { // Convert to a URI because the location of a bundle @@ -819,10 +818,9 @@ private void initializeCurrentManagedBundles() // autostart bundles and bundles installed by fileinstall. // Normalisation is needed to ensure that we don't treat (e.g.) // /tmp/foo and /tmp//foo differently. - String location = bundles[i].getLocation(); + final String location = bundles[i].getLocation(); String path = null; - if (location != null && - !location.equals(Constants.SYSTEM_BUNDLE_LOCATION)) + if (location != null && !Constants.SYSTEM_BUNDLE_LOCATION.equals(location)) { URI uri; try @@ -839,11 +837,12 @@ private void initializeCurrentManagedBundles() // blueprint:file:/tmp/foo/baa.jar -> file:/tmp/foo/baa.jar // blueprint:mvn:foo.baa/baa/0.0.1 -> mvn:foo.baa/baa/0.0.1 final String schemeSpecificPart = uri.getSchemeSpecificPart(); - // extract content behind the last colon of scheme specific path - final int offsetLastColon = schemeSpecificPart.lastIndexOf(':') + 1; + // extract content behind the 'file:' protocol of scheme specific path + final int lastIndexOfFileProtocol = schemeSpecificPart.lastIndexOf("file:"); + final int offsetFileProtocol = lastIndexOfFileProtocol > 0? lastIndexOfFileProtocol + "file:".length():0; // file:/tmp/foo/baa.jar -> /tmp/foo/baa.jar - // mvn:foo.baa/baa/0.0.1 -> foo.baa/baa/0.0.1 - path = schemeSpecificPart.substring(offsetLastColon); + // mvn:foo.baa/baa/0.0.1 -> mvn:foo.baa/baa/0.0.1 + path = schemeSpecificPart.substring(offsetFileProtocol); } else { @@ -855,15 +854,14 @@ private void initializeCurrentManagedBundles() if (path == null) { // jar.getPath is null means we could not parse the location - // as a meaningful URI or file path. e.g., location - // represented an Opaque URI. + // as a meaningful URI or file path. // We can't do any meaningful processing for this bundle. continue; } final int index = path.lastIndexOf('/'); if (index != -1 && path.startsWith(watchedDirPath)) { - Artifact artifact = new Artifact(); + final Artifact artifact = new Artifact(); artifact.setBundleId(bundles[i].getBundleId()); artifact.setChecksum(Util.loadChecksum(bundles[i], context)); artifact.setListener(null); diff --git a/fileinstall/src/test/java/org/apache/felix/fileinstall/internal/DirectoryWatcherTest.java b/fileinstall/src/test/java/org/apache/felix/fileinstall/internal/DirectoryWatcherTest.java index a3589a9b1f7..9381886bffa 100644 --- a/fileinstall/src/test/java/org/apache/felix/fileinstall/internal/DirectoryWatcherTest.java +++ b/fileinstall/src/test/java/org/apache/felix/fileinstall/internal/DirectoryWatcherTest.java @@ -22,14 +22,18 @@ import java.io.File; import java.net.URISyntaxException; import java.util.Dictionary; +import java.util.HashSet; import java.util.Hashtable; import java.util.Map; - +import java.util.Set; import junit.framework.TestCase; +import org.apache.felix.fileinstall.ArtifactListener; import org.easymock.EasyMock; +import org.junit.Assert; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleListener; +import org.osgi.framework.ServiceReference; import org.osgi.service.log.LogService; import org.osgi.service.packageadmin.PackageAdmin; @@ -255,7 +259,15 @@ public void testInvalidTempDir() throws Exception } } - public void testNonOpaqueURIOnBundleLocation() throws URISyntaxException + /** + * Test the {@link DirectoryWatcher#initializeCurrentManagedBundles()} in conjunction with a non opaque Bundle Location. + * Assert that a new created {@link Artifact} will be added into the {@link DirectoryWatcher#currentManagedArtifacts}. + * + * The {@link DirectoryWatcher#process(java.util.Set)} execution will not be called. + * This test breaks the execution in {@link Scanner#initialize(java.util.Map)}. + * @throws URISyntaxException + */ + public void testInitializeCurrentManagedBundlesNonOpaqueURIOnBundleLocation() throws URISyntaxException { final RuntimeException expectedException = new RuntimeException("expected exception to break execution on defined point."); final File watchedDirectoryFile = new File("src/test/resources/watched"); @@ -266,7 +278,7 @@ public void testNonOpaqueURIOnBundleLocation() throws URISyntaxException final String bundleLocation = "file:"+watchedDirectoryPath+'/'+bundleFileName; // break execution - Scanner scanner = new Scanner(watchedDirectoryFile) + final Scanner scanner = new Scanner(watchedDirectoryFile) { public void initialize(Map checksums) { @@ -278,7 +290,7 @@ public void initialize(Map checksums) EasyMock.expect(mockBundleContext.getBundles()).andReturn(new Bundle[]{mockBundle}); EasyMock.expect(mockBundleContext.getDataFile((String) EasyMock.anyObject())).andReturn(null).anyTimes(); EasyMock.expect(mockBundle.getLocation()).andReturn(bundleLocation).anyTimes(); - Map mockCurrentManagedArtifacts = (Map)EasyMock.createNiceMock(Map.class); + final Map mockCurrentManagedArtifacts = (Map)EasyMock.createNiceMock(Map.class); EasyMock.expect(mockCurrentManagedArtifacts.put(EasyMock.eq(bundleFile), (Artifact)EasyMock.anyObject())).andReturn(null).times(1); EasyMock.replay(new Object[]{mockBundleContext, mockBundle, mockCurrentManagedArtifacts}); @@ -290,7 +302,7 @@ public void initialize(Map checksums) dw.currentManagedArtifacts = mockCurrentManagedArtifacts; dw.scanner = scanner; try { - dw.start(); + dw.start(); } catch(RuntimeException e) { @@ -300,7 +312,15 @@ public void initialize(Map checksums) EasyMock.verify(new Object[]{mockBundleContext, mockBundle, mockCurrentManagedArtifacts}); } - public void testOpaqueURIOnBundleLocation() throws URISyntaxException + /** + * Test the {@link DirectoryWatcher#initializeCurrentManagedBundles()} in conjunction with a opaque Bundle Location. + * Assert that a new created {@link Artifact} will be added into the {@link DirectoryWatcher#currentManagedArtifacts}. + * + * The {@link DirectoryWatcher#process(java.util.Set)} execution will not be called. + * This test breaks the execution in {@link Scanner#initialize(java.util.Map)}. + * @throws URISyntaxException + */ + public void testInitializeCurrentManagedBundlesOpaqueURIOnBundleLocation() throws URISyntaxException { final RuntimeException expectedException = new RuntimeException("expected exception to break execution on defined point."); final File watchedDirectoryFile = new File("src/test/resources/watched"); @@ -345,4 +365,74 @@ public void initialize(Map checksums) EasyMock.verify(new Object[]{mockBundleContext, mockBundle, mockCurrentManagedArtifacts}); } + /** + * Test the {@link DirectoryWatcher#process(java.util.Set) } in conjunction with a opaque Bundle Location. + * Assert that no bundle refresh will be called. + * @throws URISyntaxException + */ + public void testProcessOpaqueURIOnBundleLocation() throws URISyntaxException + { + final RuntimeException expectedException = new RuntimeException("expected exception to break execution on defined point."); + final File watchedDirectoryFile = new File("src/test/resources/watched"); + final String watchedDirectoryPath = watchedDirectoryFile.getAbsolutePath(); + + final String bundleFileName = "firstjar.jar"; + final File bundleFile = new File(watchedDirectoryPath,bundleFileName); + final String bundleLocation = "blueprint:file:"+watchedDirectoryPath+'/'+bundleFileName; + + final Scanner scanner = new Scanner(watchedDirectoryFile) + { + // bypass filesystem scan and return expected bundle file + public Set/**/ scan(boolean reportImmediately) + { + Set/**/ fileSet = new HashSet/**/(1); + fileSet.add(bundleFile); + return fileSet; + } + }; + + final ArtifactListener mockArtifactListener = (ArtifactListener) EasyMock.createNiceMock(ArtifactListener.class); + EasyMock.expect(Boolean.valueOf(mockArtifactListener.canHandle(bundleFile))).andReturn(Boolean.TRUE).anyTimes(); + final ServiceReference mockServiceReference = (ServiceReference) EasyMock.createNiceMock(ServiceReference.class); + + // simulate known/installed bundles + mockBundleContext.addBundleListener((BundleListener) org.easymock.EasyMock.anyObject()); + EasyMock.expect(mockBundleContext.getBundles()).andReturn(new Bundle[]{mockBundle}); + EasyMock.expect(mockBundleContext.getDataFile((String) EasyMock.anyObject())).andReturn(null).anyTimes(); + EasyMock.expect(mockBundle.getLocation()).andReturn(bundleLocation).anyTimes(); + + EasyMock.replay(new Object[]{mockBundleContext, mockBundle,mockServiceReference, mockArtifactListener}); + + final Artifact artifact = new Artifact(); + artifact.setBundleId(42); + artifact.setChecksum(0); + artifact.setListener(mockArtifactListener); + artifact.setPath(bundleFile); + + FileInstall.listeners.put(mockServiceReference, mockArtifactListener); + + props.put(DirectoryWatcher.DIR, watchedDirectoryPath); + + dw = new DirectoryWatcher(props, mockBundleContext) { + + void refresh(Bundle[] bundles) throws InterruptedException { + Assert.fail("bundle refresh called"); + } + + }; + dw.noInitialDelay = true; + // add expected bundle and artifact to the current managed artifacts + dw.currentManagedArtifacts.put(bundleFile, artifact); + dw.scanner = scanner; + try { + dw.start(); + } + catch(RuntimeException e) + { + assertEquals(e, expectedException); + } + + EasyMock.verify(new Object[]{mockBundleContext, mockBundle,mockServiceReference, mockArtifactListener}); + } + }