From 71c7a824792f774bebe2ad512ea7d45f8f67914d Mon Sep 17 00:00:00 2001 From: Jan Mosig Date: Thu, 28 Jan 2021 18:08:50 +0100 Subject: [PATCH 1/4] [MSHADE-366] - "Access denied" during 'minimizeJar' Now ignoring directories when scanning the classpath for services. --- .../plugins/shade/filter/MinijarFilter.java | 87 ++++++++++--------- .../shade/filter/MinijarFilterTest.java | 75 ++++++++++------ 2 files changed, 97 insertions(+), 65 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java b/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java index 818339ed..48f8f189 100644 --- a/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java +++ b/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java @@ -35,6 +35,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; @@ -131,56 +133,63 @@ private void removeServices( final MavenProject project, final Clazzpath cp ) { for ( final String fileName : project.getRuntimeClasspathElements() ) { - try ( final JarFile jar = new JarFile( fileName ) ) + if ( !Files.isDirectory( Paths.get( fileName ) ) ) { - for ( final Enumeration entries = jar.entries(); entries.hasMoreElements(); ) + try ( final JarFile jar = new JarFile( fileName ) ) { - final JarEntry jarEntry = entries.nextElement(); - if ( jarEntry.isDirectory() || !jarEntry.getName().startsWith( "META-INF/services/" ) ) + for ( final Enumeration entries = jar.entries(); entries.hasMoreElements(); ) { - continue; - } - - final String serviceClassName = - jarEntry.getName().substring( "META-INF/services/".length() ); - final boolean isNeededClass = neededClasses.contains( cp.getClazz( serviceClassName ) ); - if ( !isNeededClass ) - { - continue; - } - - try ( final BufferedReader bufferedReader = - new BufferedReader( new InputStreamReader( jar.getInputStream( jarEntry ), UTF_8 ) ) ) - { - for ( String line = bufferedReader.readLine(); line != null; - line = bufferedReader.readLine() ) + final JarEntry jarEntry = entries.nextElement(); + if ( jarEntry.isDirectory() || !jarEntry.getName().startsWith( "META-INF/services/" ) ) { - final String className = line.split( "#", 2 )[0].trim(); - if ( className.isEmpty() ) - { - continue; - } - - final Clazz clazz = cp.getClazz( className ); - if ( clazz == null || !removable.contains( clazz ) ) + continue; + } + + final String serviceClassName = + jarEntry.getName().substring( "META-INF/services/".length() ); + final boolean isNeededClass = neededClasses.contains( cp.getClazz( serviceClassName ) ); + if ( !isNeededClass ) + { + continue; + } + + try ( final BufferedReader bufferedReader = + new BufferedReader( new InputStreamReader( jar.getInputStream( jarEntry ), UTF_8 ) ) ) + { + for ( String line = bufferedReader.readLine(); line != null; + line = bufferedReader.readLine() ) { - continue; + final String className = line.split( "#", 2 )[0].trim(); + if ( className.isEmpty() ) + { + continue; + } + + final Clazz clazz = cp.getClazz( className ); + if ( clazz == null || !removable.contains( clazz ) ) + { + continue; + } + + log.debug( className + " was not removed because it is a service" ); + removeClass( clazz ); + repeatScan = true; // check whether the found classes use services in turn } - - log.debug( className + " was not removed because it is a service" ); - removeClass( clazz ); - repeatScan = true; // check whether the found classes use services in turn + } + catch ( final IOException e ) + { + log.warn( e.getMessage() ); } } - catch ( final IOException e ) - { - log.warn( e.getMessage() ); - } + } + catch ( final IOException e ) + { + log.warn( e.getMessage() ); } } - catch ( final IOException e ) + else { - log.warn( e.getMessage() ); + log.debug( "Not a JAR file candidate. Ignoring classpath element '" + fileName + "'." ); } } } diff --git a/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java b/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java index bfbaee24..58e14d26 100644 --- a/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java +++ b/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java @@ -20,39 +20,48 @@ */ import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.io.File; -import java.io.IOException; -import java.util.Set; -import java.util.TreeSet; - import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.mockito.ArgumentCaptor; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Set; +import java.util.TreeSet; + public class MinijarFilterTest { + @Rule + public TemporaryFolder tempFolder = TemporaryFolder.builder().assureDeletion().build(); + private File emptyFile; + private Log log; + private ArgumentCaptor logCaptor; @Before public void init() throws IOException { - TemporaryFolder tempFolder = new TemporaryFolder(); - tempFolder.create(); this.emptyFile = tempFolder.newFile(); - + this.log = mock(Log.class); + logCaptor = ArgumentCaptor.forClass(CharSequence.class); } /** @@ -64,12 +73,8 @@ public void testWithMockProject() { assumeFalse( "Expected to run under JDK8+", System.getProperty("java.version").startsWith("1.7") ); - ArgumentCaptor logCaptor = ArgumentCaptor.forClass( CharSequence.class ); - MavenProject mavenProject = mockProject( emptyFile ); - Log log = mock( Log.class ); - MinijarFilter mf = new MinijarFilter( mavenProject, log ); mf.finished(); @@ -84,14 +89,10 @@ public void testWithMockProject() public void testWithPomProject() throws IOException { - ArgumentCaptor logCaptor = ArgumentCaptor.forClass( CharSequence.class ); - // project with pom packaging and no artifact. MavenProject mavenProject = mockProject( null ); mavenProject.setPackaging( "pom" ); - Log log = mock( Log.class ); - MinijarFilter mf = new MinijarFilter( mavenProject, log ); mf.finished(); @@ -105,7 +106,7 @@ public void testWithPomProject() } - private MavenProject mockProject( File file ) + private MavenProject mockProject( File file, String... classPathElements ) { MavenProject mavenProject = mock( MavenProject.class ); @@ -129,17 +130,18 @@ private MavenProject mockProject( File file ) when( mavenProject.getArtifact().getFile() ).thenReturn( file ); - return mavenProject; + try { + when(mavenProject.getRuntimeClasspathElements()).thenReturn(Arrays.asList(classPathElements)); + } catch (DependencyResolutionRequiredException e) { + fail("Encountered unexpected exception: " + e.getClass().getSimpleName() + ": " + e.getMessage()); + } + return mavenProject; } @Test public void finsishedShouldProduceMessageForClassesTotalNonZero() { - ArgumentCaptor logCaptor = ArgumentCaptor.forClass( CharSequence.class ); - - Log log = mock( Log.class ); - MinijarFilter m = new MinijarFilter( 1, 50, log ); m.finished(); @@ -153,10 +155,6 @@ public void finsishedShouldProduceMessageForClassesTotalNonZero() @Test public void finishedShouldProduceMessageForClassesTotalZero() { - ArgumentCaptor logCaptor = ArgumentCaptor.forClass( CharSequence.class ); - - Log log = mock( Log.class ); - MinijarFilter m = new MinijarFilter( 0, 0, log ); m.finished(); @@ -166,4 +164,29 @@ public void finishedShouldProduceMessageForClassesTotalZero() assertEquals( "Minimized 0 -> 0", logCaptor.getValue() ); } + + /** + * Check that the algorithm that removes services does not consider directories comming from the + * classpath as jar file candidates. + * + * @see https://issues.apache.org/jira/browse/MSHADE-366 + */ + @Test + public void remove_services_ignores_directories() throws Exception { + MavenProject mockedProject = mockProject(emptyFile, tempFolder.getRoot().getAbsolutePath()); + + new MinijarFilter(mockedProject, log); + + verify(log, never()).warn(logCaptor.capture()); + } + + @Test + public void remove_services_logs_ignored_items() throws Exception { + String classPathElementToIgnore = tempFolder.getRoot().getAbsolutePath(); + MavenProject mockedProject = mockProject(emptyFile, classPathElementToIgnore); + + new MinijarFilter(mockedProject, log); + + verify(log, times(1)).debug("Not a JAR file candidate. Ignoring classpath element '" + classPathElementToIgnore + "'."); + } } From 6bf666284b2c8556b135fe39c79ed50ee502eaa1 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Sat, 3 Jul 2021 12:11:06 +0700 Subject: [PATCH 2/4] [MSHADE-366] Refactor fix by @JanMosigItemis from #83 - Simplify Jan's solution from #83 in order to use 'continue' instead of nested 'if-else'. - Factor out two helper methods from 'removeServices', because that method was way too big to still be readable. - DRY-refactor Jan's new test cases into one checking two conditions. --- .../plugins/shade/filter/MinijarFilter.java | 124 ++++++++++-------- .../shade/filter/MinijarFilterTest.java | 37 +++--- 2 files changed, 84 insertions(+), 77 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java b/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java index 48f8f189..23286788 100644 --- a/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java +++ b/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java @@ -35,8 +35,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; @@ -133,63 +131,14 @@ private void removeServices( final MavenProject project, final Clazzpath cp ) { for ( final String fileName : project.getRuntimeClasspathElements() ) { - if ( !Files.isDirectory( Paths.get( fileName ) ) ) + if ( new File( fileName ).isDirectory() ) { - try ( final JarFile jar = new JarFile( fileName ) ) - { - for ( final Enumeration entries = jar.entries(); entries.hasMoreElements(); ) - { - final JarEntry jarEntry = entries.nextElement(); - if ( jarEntry.isDirectory() || !jarEntry.getName().startsWith( "META-INF/services/" ) ) - { - continue; - } - - final String serviceClassName = - jarEntry.getName().substring( "META-INF/services/".length() ); - final boolean isNeededClass = neededClasses.contains( cp.getClazz( serviceClassName ) ); - if ( !isNeededClass ) - { - continue; - } - - try ( final BufferedReader bufferedReader = - new BufferedReader( new InputStreamReader( jar.getInputStream( jarEntry ), UTF_8 ) ) ) - { - for ( String line = bufferedReader.readLine(); line != null; - line = bufferedReader.readLine() ) - { - final String className = line.split( "#", 2 )[0].trim(); - if ( className.isEmpty() ) - { - continue; - } - - final Clazz clazz = cp.getClazz( className ); - if ( clazz == null || !removable.contains( clazz ) ) - { - continue; - } - - log.debug( className + " was not removed because it is a service" ); - removeClass( clazz ); - repeatScan = true; // check whether the found classes use services in turn - } - } - catch ( final IOException e ) - { - log.warn( e.getMessage() ); - } - } - } - catch ( final IOException e ) - { - log.warn( e.getMessage() ); - } + log.debug( "Not a JAR file candidate. Ignoring classpath element '" + fileName + "'." ); + continue; } - else + if ( removeServicesFromJar( cp, neededClasses, fileName ) ) { - log.debug( "Not a JAR file candidate. Ignoring classpath element '" + fileName + "'." ); + repeatScan = true; } } } @@ -201,6 +150,69 @@ private void removeServices( final MavenProject project, final Clazzpath cp ) while ( repeatScan ); } + private boolean removeServicesFromJar( Clazzpath cp, Set neededClasses, String fileName ) + { + boolean repeatScan = false; + try ( final JarFile jar = new JarFile( fileName ) ) + { + for ( final Enumeration entries = jar.entries(); entries.hasMoreElements(); ) + { + final JarEntry jarEntry = entries.nextElement(); + if ( jarEntry.isDirectory() || !jarEntry.getName().startsWith( "META-INF/services/" ) ) + { + continue; + } + + final String serviceClassName = jarEntry.getName().substring( "META-INF/services/".length() ); + final boolean isNeededClass = neededClasses.contains( cp.getClazz( serviceClassName ) ); + if ( !isNeededClass ) + { + continue; + } + + try ( final BufferedReader configFileReader = new BufferedReader( + new InputStreamReader( jar.getInputStream( jarEntry ), UTF_8 ) ) ) + { + // check whether the found classes use services in turn + repeatScan = scanServiceProviderConfigFile( cp, configFileReader ); + } + catch ( final IOException e ) + { + log.warn( e.getMessage() ); + } + } + } + catch ( final IOException e ) + { + log.warn( e.getMessage() ); + } + return repeatScan; + } + + private boolean scanServiceProviderConfigFile( Clazzpath cp, BufferedReader configFileReader ) throws IOException + { + boolean serviceClassFound = false; + for ( String line = configFileReader.readLine(); line != null; line = configFileReader.readLine() ) + { + final String className = line.split( "#", 2 )[0].trim(); + if ( className.isEmpty() ) + { + continue; + } + + final Clazz clazz = cp.getClazz( className ); + if ( clazz == null || !removable.contains( clazz ) ) + { + continue; + } + + log.debug( className + " was not removed because it is a service" ); + removeClass( clazz ); + serviceClassFound = true; + } + return serviceClassFound; + } + private void removeClass( final Clazz clazz ) { removable.remove( clazz ); diff --git a/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java b/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java index 58e14d26..874c93e7 100644 --- a/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java +++ b/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java @@ -28,6 +28,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Set; +import java.util.TreeSet; + import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.artifact.DependencyResolutionRequiredException; @@ -39,12 +45,6 @@ import org.junit.rules.TemporaryFolder; import org.mockito.ArgumentCaptor; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Set; -import java.util.TreeSet; - public class MinijarFilterTest { @@ -166,27 +166,22 @@ public void finishedShouldProduceMessageForClassesTotalZero() } /** - * Check that the algorithm that removes services does not consider directories comming from the - * classpath as jar file candidates. - * - * @see https://issues.apache.org/jira/browse/MSHADE-366 + * Verify that directories are ignored when scanning the classpath for JARs containing services, + * but warnings are logged instead + * + * @see MSHADE-366 */ @Test - public void remove_services_ignores_directories() throws Exception { - MavenProject mockedProject = mockProject(emptyFile, tempFolder.getRoot().getAbsolutePath()); - - new MinijarFilter(mockedProject, log); - - verify(log, never()).warn(logCaptor.capture()); - } - - @Test - public void remove_services_logs_ignored_items() throws Exception { + public void removeServicesShouldIgnoreDirectories() throws Exception { String classPathElementToIgnore = tempFolder.getRoot().getAbsolutePath(); MavenProject mockedProject = mockProject(emptyFile, classPathElementToIgnore); new MinijarFilter(mockedProject, log); - verify(log, times(1)).debug("Not a JAR file candidate. Ignoring classpath element '" + classPathElementToIgnore + "'."); + verify(log, never()).warn(logCaptor.capture()); + verify(log, times(1)).debug( + "Not a JAR file candidate. Ignoring classpath element '" + classPathElementToIgnore + "'." + ); } + } From ed8ec8e2e7fed1f7cf5418afc79e77529fe3672d Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 20 Oct 2022 17:04:24 +0200 Subject: [PATCH 3/4] Another attempt to clarify the problem - do not ignore directories, print a warning as before - ignore the project's build output directory which is always returned by getRuntimeClassPathElements() --- .../plugins/shade/filter/MinijarFilter.java | 12 ++++-- .../shade/filter/MinijarFilterTest.java | 37 +++++++++++++------ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java b/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java index 23286788..a996bd46 100644 --- a/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java +++ b/src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java @@ -129,11 +129,17 @@ private void removeServices( final MavenProject project, final Clazzpath cp ) neededClasses.removeAll( removable ); try { + // getRuntimeClasspathElements returns a list of + // - the build output directory + // - all the paths to the dependencies' jars + // We thereby need to ignore the build directory because we don't want + // to remove anything from it, as it's the starting point of the + // minification process. for ( final String fileName : project.getRuntimeClasspathElements() ) { - if ( new File( fileName ).isDirectory() ) + // Ignore the build directory from this project + if ( fileName.equals( project.getBuild().getOutputDirectory() ) ) { - log.debug( "Not a JAR file candidate. Ignoring classpath element '" + fileName + "'." ); continue; } if ( removeServicesFromJar( cp, neededClasses, fileName ) ) @@ -184,7 +190,7 @@ private boolean removeServicesFromJar( Clazzpath cp, Set neededClasses, S } catch ( final IOException e ) { - log.warn( e.getMessage() ); + log.warn( "Not a JAR file candidate. Ignoring classpath element '" + fileName + "' (" + e + ")." ); } return repeatScan; } diff --git a/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java b/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java index 874c93e7..59f59bf7 100644 --- a/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java +++ b/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java @@ -30,13 +30,12 @@ import java.io.File; import java.io.IOException; -import java.util.Arrays; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.artifact.DependencyResolutionRequiredException; +import org.apache.maven.model.Build; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.junit.Before; @@ -51,6 +50,7 @@ public class MinijarFilterTest @Rule public TemporaryFolder tempFolder = TemporaryFolder.builder().assureDeletion().build(); + private File outputDirectory; private File emptyFile; private Log log; private ArgumentCaptor logCaptor; @@ -59,6 +59,7 @@ public class MinijarFilterTest public void init() throws IOException { + this.outputDirectory = tempFolder.newFolder(); this.emptyFile = tempFolder.newFile(); this.log = mock(Log.class); logCaptor = ArgumentCaptor.forClass(CharSequence.class); @@ -73,7 +74,7 @@ public void testWithMockProject() { assumeFalse( "Expected to run under JDK8+", System.getProperty("java.version").startsWith("1.7") ); - MavenProject mavenProject = mockProject( emptyFile ); + MavenProject mavenProject = mockProject( outputDirectory, emptyFile ); MinijarFilter mf = new MinijarFilter( mavenProject, log ); @@ -90,7 +91,7 @@ public void testWithPomProject() throws IOException { // project with pom packaging and no artifact. - MavenProject mavenProject = mockProject( null ); + MavenProject mavenProject = mockProject( outputDirectory, null ); mavenProject.setPackaging( "pom" ); MinijarFilter mf = new MinijarFilter( mavenProject, log ); @@ -106,7 +107,7 @@ public void testWithPomProject() } - private MavenProject mockProject( File file, String... classPathElements ) + private MavenProject mockProject( File outputDirectory, File file, String... classPathElements ) { MavenProject mavenProject = mock( MavenProject.class ); @@ -130,8 +131,19 @@ private MavenProject mockProject( File file, String... classPathElements ) when( mavenProject.getArtifact().getFile() ).thenReturn( file ); + Build build = new Build(); + build.setOutputDirectory( outputDirectory.toString() ); + + List classpath = new ArrayList<>(); + classpath.add( outputDirectory.toString() ); + if ( file != null ) + { + classpath.add(file.toString()); + } + classpath.addAll( Arrays.asList( classPathElements ) ); + when( mavenProject.getBuild() ).thenReturn( build ); try { - when(mavenProject.getRuntimeClasspathElements()).thenReturn(Arrays.asList(classPathElements)); + when(mavenProject.getRuntimeClasspathElements()).thenReturn(classpath); } catch (DependencyResolutionRequiredException e) { fail("Encountered unexpected exception: " + e.getClass().getSimpleName() + ": " + e.getMessage()); } @@ -173,14 +185,15 @@ public void finishedShouldProduceMessageForClassesTotalZero() */ @Test public void removeServicesShouldIgnoreDirectories() throws Exception { - String classPathElementToIgnore = tempFolder.getRoot().getAbsolutePath(); - MavenProject mockedProject = mockProject(emptyFile, classPathElementToIgnore); + String classPathElementToIgnore = tempFolder.newFolder().getAbsolutePath(); + MavenProject mockedProject = mockProject( outputDirectory, emptyFile, classPathElementToIgnore ); new MinijarFilter(mockedProject, log); - verify(log, never()).warn(logCaptor.capture()); - verify(log, times(1)).debug( - "Not a JAR file candidate. Ignoring classpath element '" + classPathElementToIgnore + "'." + verify(log, times(1)).warn( + "Not a JAR file candidate. Ignoring classpath element '" + classPathElementToIgnore + + "' (" + new java.io.FileNotFoundException( classPathElementToIgnore + " (Is a directory)" ) + + ")." ); } From 5213c6b45ccc5e204dd38febe9b821885a67ab0c Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 20 Oct 2022 17:49:01 +0200 Subject: [PATCH 4/4] Fix the test to work on all platforms, irrespective of the actual exception sent by the JDK --- .../shade/filter/MinijarFilterTest.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java b/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java index 59f59bf7..25be4d8a 100644 --- a/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java +++ b/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java @@ -19,18 +19,25 @@ * under the License. */ +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.jar.JarOutputStream; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; @@ -52,6 +59,7 @@ public class MinijarFilterTest private File outputDirectory; private File emptyFile; + private File jarFile; private Log log; private ArgumentCaptor logCaptor; @@ -61,6 +69,8 @@ public void init() { this.outputDirectory = tempFolder.newFolder(); this.emptyFile = tempFolder.newFile(); + this.jarFile = tempFolder.newFile(); + new JarOutputStream( new FileOutputStream( this.jarFile ) ).close(); this.log = mock(Log.class); logCaptor = ArgumentCaptor.forClass(CharSequence.class); } @@ -186,15 +196,14 @@ public void finishedShouldProduceMessageForClassesTotalZero() @Test public void removeServicesShouldIgnoreDirectories() throws Exception { String classPathElementToIgnore = tempFolder.newFolder().getAbsolutePath(); - MavenProject mockedProject = mockProject( outputDirectory, emptyFile, classPathElementToIgnore ); + MavenProject mockedProject = mockProject( outputDirectory, jarFile, classPathElementToIgnore ); new MinijarFilter(mockedProject, log); - verify(log, times(1)).warn( - "Not a JAR file candidate. Ignoring classpath element '" + classPathElementToIgnore - + "' (" + new java.io.FileNotFoundException( classPathElementToIgnore + " (Is a directory)" ) - + ")." - ); + verify( log, times( 1 ) ).warn( logCaptor.capture() ); + + assertThat( logCaptor.getValue().toString(), startsWith( + "Not a JAR file candidate. Ignoring classpath element '" + classPathElementToIgnore + "' (" ) ); } }