This is an automated email from the ASF dual-hosted git repository. hboutemy pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven-osgi.git
commit 589db0ef32f82cf7a165acbe2f6cfbc48e5a9a3d Author: Carlos Sanchez Gonzalez <car...@apache.org> AuthorDate: Tue Jul 10 16:03:03 2007 +0000 Promoting maven-osgi out of the sandbox git-svn-id: https://svn.apache.org/repos/asf/maven/shared/trunk@554988 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 69 +++++ .../shared/osgi/DefaultMaven2OsgiConverter.java | 320 +++++++++++++++++++++ .../shared/osgi/ManifestReadingException.java | 50 ++++ .../maven/shared/osgi/Maven2OsgiConverter.java | 58 ++++ .../maven/shared/osgi/Maven2OsgiConverterTest.java | 142 +++++++++ src/test/resources/commons-logging-1.1.jar | Bin 0 -> 52915 bytes src/test/resources/junit-3.8.2.jar | Bin 0 -> 120640 bytes src/test/resources/test-1.jar | Bin 0 -> 2426 bytes src/test/resources/xml-apis-1.0.b2.jar | Bin 0 -> 109318 bytes 9 files changed, 639 insertions(+) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..777a507 --- /dev/null +++ b/pom.xml @@ -0,0 +1,69 @@ +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <parent> + <groupId>org.apache.maven.shared</groupId> + <artifactId>maven-shared-components</artifactId> + <version>7</version> + </parent> + + <modelVersion>4.0.0</modelVersion> + <artifactId>maven-osgi</artifactId> + <version>0.2.0-SNAPSHOT</version> + <name>Maven OSGi integration</name> + <description>Library for Maven-OSGi integration</description> + + <dependencies> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-project</artifactId> + <version>2.0.6</version> + </dependency> + <dependency> + <groupId>biz.aQute</groupId> + <artifactId>bndlib</artifactId> + <version>0.0.145</version> + </dependency> + <dependency> + <groupId>org.apache.maven.shared</groupId> + <artifactId>maven-plugin-testing-harness</artifactId> + <version>1.0</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-maven-plugin</artifactId> + <version>1.3.5</version> + <executions> + <execution> + <goals> + <goal>descriptor</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/src/main/java/org/apache/maven/shared/osgi/DefaultMaven2OsgiConverter.java b/src/main/java/org/apache/maven/shared/osgi/DefaultMaven2OsgiConverter.java new file mode 100644 index 0000000..6b0402d --- /dev/null +++ b/src/main/java/org/apache/maven/shared/osgi/DefaultMaven2OsgiConverter.java @@ -0,0 +1,320 @@ +/* + * 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.apache.maven.shared.osgi; + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarFile; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; + +import org.apache.maven.artifact.Artifact; + +import aQute.lib.osgi.Analyzer; + +/** + * Default implementation of {@link Maven2OsgiConverter} + * + * @plexus.component + * + * @author <a href="mailto:car...@apache.org">Carlos Sanchez</a> + * @version $Id$ + */ +public class DefaultMaven2OsgiConverter + implements Maven2OsgiConverter +{ + + /** Bundle-Version must match this pattern */ + private static final Pattern OSGI_VERSION_PATTERN = Pattern + .compile( "[0-9]+(\\.[0-9]+(\\.[0-9]+(\\.[0-9A-Za-z_-]+)?)?)?" ); + + /** pattern used to change - to . */ + // private static final Pattern P_VERSION = Pattern.compile("([0-9]+(\\.[0-9])*)-(.*)"); + /** pattern that matches strings that contain only numbers */ + private static final Pattern ONLY_NUMBERS = Pattern.compile( "[0-9]+" ); + + private static final String FILE_SEPARATOR = System.getProperty( "file.separator" ); + + private String getBundleSymbolicName( String groupId, String artifactId ) + { + return groupId + "." + artifactId; + } + + /** + * Get the symbolic name as groupId + "." + artifactId, with the following exceptions + * <ul> + * <li>if artifact.getFile is not null and the jar contains a OSGi Manifest with + * Bundle-SymbolicName property then that value is returned</li> + * <li>if groupId has only one section (no dots) and artifact.getFile is not null then the + * first package name with classes is returned. eg. commons-logging:commons-logging -> + * org.apache.commons.logging</li> + * <li>if artifactId is equal to last section of groupId then groupId is returned. eg. + * org.apache.maven:maven -> org.apache.maven</li> + * <li>if artifactId starts with last section of groupId that portion is removed. eg. + * org.apache.maven:maven-core -> org.apache.maven.core</li> + * </ul> + */ + public String getBundleSymbolicName( Artifact artifact ) + { + if ( ( artifact.getFile() != null ) && artifact.getFile().exists() ) + { + Analyzer analyzer = new Analyzer(); + + try + { + JarFile jar = new JarFile( artifact.getFile(), false ); + + if ( jar.getManifest() != null ) + { + String symbolicNameAttribute = jar.getManifest().getMainAttributes() + .getValue( Analyzer.BUNDLE_SYMBOLICNAME ); + Map bundleSymbolicNameHeader = analyzer.parseHeader( symbolicNameAttribute ); + + Iterator it = bundleSymbolicNameHeader.keySet().iterator(); + if ( it.hasNext() ) + { + return (String) it.next(); + } + } + } + catch ( IOException e ) + { + throw new ManifestReadingException( "Error reading manifest in jar " + + artifact.getFile().getAbsolutePath(), e ); + } + } + + int i = artifact.getGroupId().lastIndexOf( '.' ); + if ( ( i < 0 ) && ( artifact.getFile() != null ) && artifact.getFile().exists() ) + { + String groupIdFromPackage = getGroupIdFromPackage( artifact.getFile() ); + if ( groupIdFromPackage != null ) + { + return groupIdFromPackage; + } + } + String lastSection = artifact.getGroupId().substring( ++i ); + if ( artifact.getArtifactId().equals( lastSection ) ) + { + return artifact.getGroupId(); + } + if ( artifact.getArtifactId().startsWith( lastSection ) ) + { + String artifactId = artifact.getArtifactId().substring( lastSection.length() + 1 ); + if ( Character.isLetterOrDigit( artifactId.charAt( 0 ) ) ) + { + return getBundleSymbolicName( artifact.getGroupId(), artifactId ); + } + else + { + return getBundleSymbolicName( artifact.getGroupId(), artifactId.substring( 1 ) ); + } + } + return getBundleSymbolicName( artifact.getGroupId(), artifact.getArtifactId() ); + } + + private String getGroupIdFromPackage( File artifactFile ) + { + try + { + /* get package names from jar */ + Set packageNames = new HashSet(); + JarFile jar = new JarFile( artifactFile, false ); + Enumeration entries = jar.entries(); + while ( entries.hasMoreElements() ) + { + ZipEntry entry = (ZipEntry) entries.nextElement(); + if ( entry.getName().endsWith( ".class" ) ) + { + File f = new File( entry.getName() ); + String packageName = f.getParent(); + if ( packageName != null ) + { + packageNames.add( packageName ); + } + } + } + + /* find the top package */ + String[] groupIdSections = null; + for ( Iterator it = packageNames.iterator(); it.hasNext(); ) + { + String packageName = (String) it.next(); + + String[] packageNameSections = packageName.split( "\\" + FILE_SEPARATOR ); + if ( groupIdSections == null ) + { + /* first candidate */ + groupIdSections = packageNameSections; + } + else + // if ( packageNameSections.length < groupIdSections.length ) + { + /* + * find the common portion of current package and previous selected groupId + */ + int i; + for ( i = 0; ( i < packageNameSections.length ) && ( i < groupIdSections.length ); i++ ) + { + if ( !packageNameSections[i].equals( groupIdSections[i] ) ) + { + break; + } + } + groupIdSections = new String[i]; + System.arraycopy( packageNameSections, 0, groupIdSections, 0, i ); + } + } + + if ( ( groupIdSections == null ) || ( groupIdSections.length == 0 ) ) + { + return null; + } + + StringBuffer sb = new StringBuffer(); + for ( int i = 0; i < groupIdSections.length; i++ ) + { + sb.append( groupIdSections[i] ); + if ( i < groupIdSections.length - 1 ) + { + sb.append( '.' ); + } + } + return sb.toString(); + } + catch ( IOException e ) + { + /* we took all the precautions to avoid this */ + throw new RuntimeException( e ); + } + } + + public String getBundleFileName( Artifact artifact ) + { + return getBundleSymbolicName( artifact ) + "_" + getVersion( artifact.getVersion() ) + ".jar"; + } + + public String getVersion( Artifact artifact ) + { + return getVersion( artifact.getVersion() ); + } + + public String getVersion( String version ) + { + String osgiVersion; + + // Matcher m = P_VERSION.matcher(version); + // if (m.matches()) { + // osgiVersion = m.group(1) + "." + m.group(3); + // } + + /* TODO need a regexp guru here */ + + Matcher m; + + /* if it's already OSGi compliant don't touch it */ + m = OSGI_VERSION_PATTERN.matcher( version ); + if ( m.matches() ) + { + return version; + } + + osgiVersion = version; + + /* check for dated snapshot versions with only major or major and minor */ + Pattern DATED_SNAPSHOT = Pattern.compile( "([0-9])(\\.([0-9]))?(\\.([0-9]))?\\-([0-9]{8}\\.[0-9]{6}\\-[0-9]*)" ); + m = DATED_SNAPSHOT.matcher( osgiVersion ); + if ( m.matches() ) + { + String major = m.group( 1 ); + String minor = ( m.group( 3 ) != null ) ? m.group( 3 ) : "0"; + String service = ( m.group( 5 ) != null ) ? m.group( 5 ) : "0"; + String qualifier = m.group( 6 ).replaceAll( "-", "_" ).replaceAll( "\\.", "_" ); + osgiVersion = major + "." + minor + "." + service + "." + qualifier; + } + + /* else transform first - to . and others to _ */ + osgiVersion = osgiVersion.replaceFirst( "-", "\\." ); + osgiVersion = osgiVersion.replaceAll( "-", "_" ); + m = OSGI_VERSION_PATTERN.matcher( osgiVersion ); + if ( m.matches() ) + { + return osgiVersion; + } + + /* remove dots in the middle of the qualifier */ + Pattern DOTS_IN_QUALIFIER = Pattern.compile( "([0-9])(\\.[0-9])?\\.([0-9A-Za-z_-]+)\\.([0-9A-Za-z_-]+)" ); + m = DOTS_IN_QUALIFIER.matcher( osgiVersion ); + if ( m.matches() ) + { + String s1 = m.group( 1 ); + String s2 = m.group( 2 ); + String s3 = m.group( 3 ); + String s4 = m.group( 4 ); + + Matcher qualifierMatcher = ONLY_NUMBERS.matcher( s3 ); + /* + * if last portion before dot is only numbers then it's not in the middle of the + * qualifier + */ + if ( !qualifierMatcher.matches() ) + { + osgiVersion = s1 + s2 + "." + s3 + "_" + s4; + } + } + + /* convert 1.string into 1.0.0.string and 1.2.string into 1.2.0.string */ + Pattern NEED_TO_FILL_ZEROS = Pattern.compile( "([0-9])(\\.([0-9]))?\\.([0-9A-Za-z_-]+)" ); + m = NEED_TO_FILL_ZEROS.matcher( osgiVersion ); + if ( m.matches() ) + { + String major = m.group( 1 ); + String minor = ( m.group( 3 ) != null ) ? m.group( 3 ) : "0"; + String service = "0"; + String qualifier = m.group( 4 ); + + Matcher qualifierMatcher = ONLY_NUMBERS.matcher( qualifier ); + /* if last portion is only numbers then it's not a qualifier */ + if ( !qualifierMatcher.matches() ) + { + osgiVersion = major + "." + minor + "." + service + "." + qualifier; + } + } + + m = OSGI_VERSION_PATTERN.matcher( osgiVersion ); + /* if still its not OSGi version then add everything as qualifier */ + if ( !m.matches() ) + { + String major = "0"; + String minor = "0"; + String service = "0"; + String qualifier = osgiVersion.replaceAll( "\\.", "_" ); + osgiVersion = major + "." + minor + "." + service + "." + qualifier; + } + + return osgiVersion; + } + +} diff --git a/src/main/java/org/apache/maven/shared/osgi/ManifestReadingException.java b/src/main/java/org/apache/maven/shared/osgi/ManifestReadingException.java new file mode 100644 index 0000000..bf1b729 --- /dev/null +++ b/src/main/java/org/apache/maven/shared/osgi/ManifestReadingException.java @@ -0,0 +1,50 @@ +/* + * 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.apache.maven.shared.osgi; + +/** + * Exception while reading the manifest. Encapsulates an IOException to make it runtime + * + * @author <a href="mailto:car...@apache.org">Carlos Sanchez</a> + * @version $Id$ + */ +public class ManifestReadingException + extends RuntimeException +{ + + public ManifestReadingException() + { + super(); + } + + public ManifestReadingException( String message, Throwable cause ) + { + super( message, cause ); + } + + public ManifestReadingException( String message ) + { + super( message ); + } + + public ManifestReadingException( Throwable cause ) + { + super( cause ); + } +} diff --git a/src/main/java/org/apache/maven/shared/osgi/Maven2OsgiConverter.java b/src/main/java/org/apache/maven/shared/osgi/Maven2OsgiConverter.java new file mode 100644 index 0000000..522472f --- /dev/null +++ b/src/main/java/org/apache/maven/shared/osgi/Maven2OsgiConverter.java @@ -0,0 +1,58 @@ +/* + * 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.apache.maven.shared.osgi; + +import org.apache.maven.artifact.Artifact; + +/** + * Converter from Maven groupId,artifactId and versions to OSGi Bundle-SymbolicName and version + * + * @author <a href="mailto:car...@apache.org">Carlos Sanchez</a> + * @version $Id$ + */ +public interface Maven2OsgiConverter +{ + + /** + * Get the OSGi symbolic name for the artifact + * + * @param artifact + * @return the Bundle-SymbolicName manifest property + */ + String getBundleSymbolicName( Artifact artifact ); + + String getBundleFileName( Artifact artifact ); + + /** + * Convert a Maven version into an OSGi compliant version + * + * @param artifact Maven artifact + * @return the OSGi version + */ + String getVersion( Artifact artifact ); + + /** + * Convert a Maven version into an OSGi compliant version + * + * @param version Maven version + * @return the OSGi version + */ + String getVersion( String version ); + +} \ No newline at end of file diff --git a/src/test/java/org/apache/maven/shared/osgi/Maven2OsgiConverterTest.java b/src/test/java/org/apache/maven/shared/osgi/Maven2OsgiConverterTest.java new file mode 100644 index 0000000..6923c6b --- /dev/null +++ b/src/test/java/org/apache/maven/shared/osgi/Maven2OsgiConverterTest.java @@ -0,0 +1,142 @@ +/* + * 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.apache.maven.shared.osgi; + +import java.io.File; + +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.codehaus.plexus.PlexusTestCase; + +/** + * Test for {@link DefaultMaven2OsgiConverter} + * + * @author <a href="mailto:car...@apache.org">Carlos Sanchez</a> + * @version $Id$ + */ +public class Maven2OsgiConverterTest + extends PlexusTestCase +{ + + private Maven2OsgiConverter maven2Osgi = new DefaultMaven2OsgiConverter(); + + public void testGetBundleSymbolicName() + { + ArtifactStub artifact = getTestArtifact(); + String s = maven2Osgi.getBundleSymbolicName( artifact ); + assertEquals( "org.apache.commons.logging", s ); + + artifact.setGroupId( "org.apache.commons" ); + s = maven2Osgi.getBundleSymbolicName( artifact ); + assertEquals( "org.apache.commons.logging", s ); + + artifact.setGroupId( "org.apache.commons.commons-logging" ); + s = maven2Osgi.getBundleSymbolicName( artifact ); + assertEquals( "org.apache.commons.commons-logging", s ); + + artifact.setFile( getTestFile( "junit-3.8.2.jar" ) ); + artifact.setGroupId( "junit" ); + s = maven2Osgi.getBundleSymbolicName( artifact ); + assertEquals( "junit", s ); + + artifact.setFile( getTestFile( "xml-apis-1.0.b2.jar" ) ); + artifact.setGroupId( "xml-apis" ); + artifact.setArtifactId( "a" ); + s = maven2Osgi.getBundleSymbolicName( artifact ); + assertEquals( "xml-apis.a", s ); + + artifact.setFile( getTestFile( "test-1.jar" ) ); + artifact.setGroupId( "test" ); + artifact.setArtifactId( "test" ); + s = maven2Osgi.getBundleSymbolicName( artifact ); + assertEquals( "test", s ); + } + + public void testGetBundleFileName() + { + ArtifactStub artifact = getTestArtifact(); + String s = maven2Osgi.getBundleFileName( artifact ); + assertEquals( "org.apache.commons.logging_1.1.jar", s ); + } + + public void testGetVersion() + { + ArtifactStub artifact = getTestArtifact(); + String s = maven2Osgi.getVersion( artifact ); + assertEquals( "1.1", s ); + } + + public void testConvertVersionToOsgi() + { + String osgiVersion; + + osgiVersion = maven2Osgi.getVersion( "2.1.0-SNAPSHOT" ); + assertEquals( "2.1.0.SNAPSHOT", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "2.1-SNAPSHOT" ); + assertEquals( "2.1.0.SNAPSHOT", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "2-SNAPSHOT" ); + assertEquals( "2.0.0.SNAPSHOT", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "2" ); + assertEquals( "2", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "2.1" ); + assertEquals( "2.1", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "2.1.3" ); + assertEquals( "2.1.3", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "2.1.3.4" ); + assertEquals( "2.1.3.4", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "4aug2000r7-dev" ); + assertEquals( "0.0.0.4aug2000r7_dev", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "1.1-alpha-2" ); + assertEquals( "1.1.0.alpha_2", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "1.0-alpha-16-20070122.203121-13" ); + assertEquals( "1.0.0.alpha_16_20070122_203121_13", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "1.0-20070119.021432-1" ); + assertEquals( "1.0.0.20070119_021432_1", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "1-20070119.021432-1" ); + assertEquals( "1.0.0.20070119_021432_1", osgiVersion ); + + osgiVersion = maven2Osgi.getVersion( "1.4.1-20070217.082013-7" ); + assertEquals( "1.4.1.20070217_082013_7", osgiVersion ); + } + + private ArtifactStub getTestArtifact() + { + ArtifactStub a = new ArtifactStub(); + a.setGroupId( "commons-logging" ); + a.setArtifactId( "commons-logging" ); + a.setVersion( "1.1" ); + a.setFile( getTestFile( "commons-logging-1.1.jar" ) ); + return a; + } + + public static File getTestFile( String fileName ) + { + return PlexusTestCase.getTestFile( "src/test/resources/" + fileName ); + } +} diff --git a/src/test/resources/commons-logging-1.1.jar b/src/test/resources/commons-logging-1.1.jar new file mode 100644 index 0000000..2ff9bbd Binary files /dev/null and b/src/test/resources/commons-logging-1.1.jar differ diff --git a/src/test/resources/junit-3.8.2.jar b/src/test/resources/junit-3.8.2.jar new file mode 100644 index 0000000..c8f711d Binary files /dev/null and b/src/test/resources/junit-3.8.2.jar differ diff --git a/src/test/resources/test-1.jar b/src/test/resources/test-1.jar new file mode 100644 index 0000000..fb1d7f6 Binary files /dev/null and b/src/test/resources/test-1.jar differ diff --git a/src/test/resources/xml-apis-1.0.b2.jar b/src/test/resources/xml-apis-1.0.b2.jar new file mode 100644 index 0000000..ad33a5a Binary files /dev/null and b/src/test/resources/xml-apis-1.0.b2.jar differ -- To stop receiving notification emails like this one, please contact "commits@maven.apache.org" <commits@maven.apache.org>.