Repository: tomee Updated Branches: refs/heads/develop b8054883f -> caa0edbc3
support in MVN resolver of LATEST and LATEST-SNAPSHOT Project: http://git-wip-us.apache.org/repos/asf/tomee/repo Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/caa0edbc Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/caa0edbc Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/caa0edbc Branch: refs/heads/develop Commit: caa0edbc3dce86738a4e31455ee1928cdb89e045 Parents: b805488 Author: Romain Manni-Bucau <[email protected]> Authored: Thu Jan 8 11:48:44 2015 +0100 Committer: Romain Manni-Bucau <[email protected]> Committed: Thu Jan 8 11:48:44 2015 +0100 ---------------------------------------------------------------------- .../loader/provisining/MavenResolver.java | 90 ++++++++++++++------ .../provisining/ProvisioningResolver.java | 59 ++++++++++++- .../loader/provisining/MavenResolverTest.java | 81 ++++++++++++++++++ 3 files changed, 201 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tomee/blob/caa0edbc/container/openejb-loader/src/main/java/org/apache/openejb/loader/provisining/MavenResolver.java ---------------------------------------------------------------------- diff --git a/container/openejb-loader/src/main/java/org/apache/openejb/loader/provisining/MavenResolver.java b/container/openejb-loader/src/main/java/org/apache/openejb/loader/provisining/MavenResolver.java index 873d527..033320e 100644 --- a/container/openejb-loader/src/main/java/org/apache/openejb/loader/provisining/MavenResolver.java +++ b/container/openejb-loader/src/main/java/org/apache/openejb/loader/provisining/MavenResolver.java @@ -62,7 +62,9 @@ public class MavenResolver implements ArchiveResolver, ProvisioningResolverAware } private String sanitize(final String url) { - return url.substring(prefix().length() + 1).replace(":", "/"); + final int sep = url.indexOf('!') + 1; + String value = url.substring(prefix().length() + 1); + return value.substring(0, sep) + value.substring(sep).replace(":", "/"); } @Override @@ -108,11 +110,6 @@ public class MavenResolver implements ArchiveResolver, ProvisioningResolverAware } else { final int repoIdx = raw.lastIndexOf("!"); toParse = raw.substring(repoIdx + 1); - final String repo = raw.substring(0, repoIdx); - builder.append(repo); - if (!repo.endsWith("/")) { - builder.append("/"); - } } builder.append(mvnArtifactPath(toParse, base)); @@ -131,7 +128,6 @@ public class MavenResolver implements ArchiveResolver, ProvisioningResolverAware } private String mvnArtifactPath(final String toParse, final String snapshotBase) throws MalformedURLException { - final StringBuilder builder = new StringBuilder(); final String[] segments = toParse.split("/"); if (segments.length < 3) { throw new MalformedURLException("Invalid path. " + toParse); @@ -141,24 +137,41 @@ public class MavenResolver implements ArchiveResolver, ProvisioningResolverAware if (group.trim().isEmpty()) { throw new MalformedURLException("Invalid groupId. " + toParse); } - builder.append(group.replace('.', '/')).append("/"); final String artifact = segments[1]; if (artifact.trim().isEmpty()) { throw new MalformedURLException("Invalid artifactId. " + toParse); } - builder.append(artifact).append("/"); - final String version = segments[2]; + String version = segments[2]; if (version.trim().isEmpty()) { throw new MalformedURLException("Invalid artifactId. " + toParse); } - builder.append(version).append("/"); + String base = snapshotBase == null || snapshotBase.isEmpty() ? "" : (snapshotBase + (!snapshotBase.endsWith("/") ? "/" : "")); + + if ("LATEST".equals(version) || "LATEST-SNAPSHOT".equals(version)) { + final String meta = base + group + "/" + artifact + "/maven-metadata.xml"; + final URL url = new URL(meta); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + InputStream is = null; + try { + is = resolveStream(url.toExternalForm()); + if (is == null) { + throw new IllegalArgumentException("can't resolve " + url); + } + IO.copy(is, out); + version = extractRealVersion(version, out); + } catch (final Exception e) { + // no-op + } finally { + IO.close(is); + } + } String artifactVersion; - if (snapshotBase != null && snapshotBase.startsWith("http") && version.endsWith(SNAPSHOT_SUFFIX)) { - final String meta = snapshotBase + builder.toString() + "maven-metadata.xml"; + if (version.endsWith("-SNAPSHOT")) { + final String meta = base + group + "/" + artifact + "/" + version + "/maven-metadata.xml"; final URL url = new URL(meta); final ByteArrayOutputStream out = new ByteArrayOutputStream(); InputStream is = null; @@ -188,8 +201,11 @@ public class MavenResolver implements ArchiveResolver, ProvisioningResolverAware fullClassifier = "-" + segments[4]; } + StringBuilder builder = new StringBuilder(base); + builder.append(group.replace('.', '/')).append("/"); + builder.append(artifact).append("/"); + builder.append(version).append("/"); builder.append(artifact).append("-").append(artifactVersion); - if (fullClassifier != null) { builder.append(fullClassifier); } @@ -197,13 +213,30 @@ public class MavenResolver implements ArchiveResolver, ProvisioningResolverAware return builder.append(".").append(type).toString(); } + private static String extractRealVersion(String version, ByteArrayOutputStream out) { + final QuickMvnMetadataParser handler = new QuickMvnMetadataParser(); + try { + final SAXParser parser = FACTORY.newSAXParser(); + parser.parse(new ByteArrayInputStream(out.toByteArray()), handler); + if (!version.endsWith(SNAPSHOT_SUFFIX) && handler.release != null) { + version = handler.release.toString(); + } else if (handler.latest != null) { + version = handler.latest.toString(); + } + } catch (final Exception e) { + // no-op: not parseable so ignoring + } + return version; + } + private static String extractLastSnapshotVersion(final String defaultVersion, final InputStream metadata) { final QuickMvnMetadataParser handler = new QuickMvnMetadataParser(); try { final SAXParser parser = FACTORY.newSAXParser(); parser.parse(metadata, handler); if (handler.timestamp != null && handler.buildNumber != null) { - return defaultVersion.substring(0, defaultVersion.length() - SNAPSHOT_SUFFIX.length()) + "-" + handler.timestamp.toString() + "-" + handler.buildNumber.toString(); + return defaultVersion.substring(0, defaultVersion.length() - SNAPSHOT_SUFFIX.length()) + + "-" + handler.timestamp.toString() + "-" + handler.buildNumber.toString(); } } catch (final Exception e) { // no-op: not parseable so ignoring @@ -217,38 +250,39 @@ public class MavenResolver implements ArchiveResolver, ProvisioningResolverAware } private static class QuickMvnMetadataParser extends DefaultHandler { - private boolean readTs; - private boolean readBn; private StringBuilder timestamp; private StringBuilder buildNumber; + private StringBuilder latest; + private StringBuilder release; + private StringBuilder text; @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { if ("timestamp".equalsIgnoreCase(qName)) { - readTs = true; timestamp = new StringBuilder(); + text = timestamp; } else if ("buildNumber".equalsIgnoreCase(qName)) { - readBn = true; buildNumber = new StringBuilder(); + text = buildNumber; + } else if ("latest".equalsIgnoreCase(qName)) { + latest = new StringBuilder(); + text = latest; + } else if ("release".equalsIgnoreCase(qName)) { + release = new StringBuilder(); + text = release; } } @Override public void characters(final char[] ch, final int start, final int length) throws SAXException { - if (readBn && buildNumber != null) { - buildNumber.append(new String(ch, start, length)); - } else if (readTs && timestamp != null) { - timestamp.append(new String(ch, start, length)); + if (text != null) { + text.append(new String(ch, start, length)); } } public void endElement(final String uri, final String localName, final String qName) throws SAXException { - if ("timestamp".equalsIgnoreCase(qName)) { - readTs = false; - } else if ("buildNumber".equalsIgnoreCase(qName)) { - readBn = false; - } + text = null; } } http://git-wip-us.apache.org/repos/asf/tomee/blob/caa0edbc/container/openejb-loader/src/main/java/org/apache/openejb/loader/provisining/ProvisioningResolver.java ---------------------------------------------------------------------- diff --git a/container/openejb-loader/src/main/java/org/apache/openejb/loader/provisining/ProvisioningResolver.java b/container/openejb-loader/src/main/java/org/apache/openejb/loader/provisining/ProvisioningResolver.java index 6b5d3a3..2d92341 100644 --- a/container/openejb-loader/src/main/java/org/apache/openejb/loader/provisining/ProvisioningResolver.java +++ b/container/openejb-loader/src/main/java/org/apache/openejb/loader/provisining/ProvisioningResolver.java @@ -22,6 +22,8 @@ import org.apache.openejb.loader.SystemInstance; import java.io.BufferedInputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; @@ -193,14 +195,69 @@ public class ProvisioningResolver { // used when a resolver wants to use a local file by calling back this facade resolver public static final class LocalInputStream extends InputStream { private final String path; + private FileInputStream stream; private LocalInputStream(final String path) { this.path = path; } + private FileInputStream stream() { + if (stream == null) { + try { + stream = new FileInputStream(path); + } catch (final FileNotFoundException e) { + throw new IllegalStateException(e); + } + } + return stream; + } + @Override public int read() throws IOException { - throw new UnsupportedOperationException(); + return stream().read(); + } + + @Override + public int read(final byte[] b) throws IOException { + return stream().read(b); + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + return stream().read(b, off, len); + } + + @Override + public synchronized void mark(final int readlimit) { + stream().mark(readlimit); + } + + @Override + public int available() throws IOException { + return stream().available(); + } + + @Override + public long skip(final long n) throws IOException { + return stream().skip(n); + } + + @Override + public synchronized void reset() throws IOException { + stream().reset(); + } + + @Override + public boolean markSupported() { + return stream().markSupported(); + } + + @Override + public void close() throws IOException { + if (stream != null) { + stream.close(); + } + super.close(); } } } http://git-wip-us.apache.org/repos/asf/tomee/blob/caa0edbc/container/openejb-loader/src/test/java/org/apache/openejb/loader/provisining/MavenResolverTest.java ---------------------------------------------------------------------- diff --git a/container/openejb-loader/src/test/java/org/apache/openejb/loader/provisining/MavenResolverTest.java b/container/openejb-loader/src/test/java/org/apache/openejb/loader/provisining/MavenResolverTest.java new file mode 100644 index 0000000..2fbdfad --- /dev/null +++ b/container/openejb-loader/src/test/java/org/apache/openejb/loader/provisining/MavenResolverTest.java @@ -0,0 +1,81 @@ +/* + * 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.openejb.loader.provisining; + +import org.apache.openejb.loader.Files; +import org.apache.openejb.loader.IO; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.Collections; +import java.util.jar.JarFile; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class MavenResolverTest { + private MavenResolver resolver; + + @Before + public void init() { + resolver = new MavenResolver(); + resolver.setResolver(new ProvisioningResolver()); + } + + @Test + public void local() throws Exception { + assertEquals(ProvisioningResolver.LocalInputStream.class.getName(), resolver.resolve("mvn:junit:junit:4.11:jar").getClass().getName()); // use version of the pom to ensure it is local + } + + @Test + public void resolve() throws Exception { + File file = new File("target/test/foo.jar"); + Files.remove(file); + Files.mkdirs(file.getParentFile()); + FileOutputStream to = new FileOutputStream(file); + IO.copy(resolver.resolve("mvn:junit:junit:4.12:jar"), to); + IO.close(to); + assertTrue(file.exists()); + assertTrue(Collections.list(new JarFile(file).entries()).size() > 300 /* 323 */); // just check it is not an error page + } + + @Test + public void customRepo() throws Exception { + File file = new File("target/test/foo.jar"); + Files.remove(file); + Files.mkdirs(file.getParentFile()); + FileOutputStream to = new FileOutputStream(file); + IO.copy(resolver.resolve("mvn:http://repo1.maven.org/maven2/!junit:junit:4.12:jar"), to); + IO.close(to); + assertTrue(file.exists()); + assertTrue(Collections.list(new JarFile(file).entries()).size() > 300 /* 323 */); // just check it is not an error page + } + + @Test + public void latest() throws Exception { + File file = new File("target/test/foo.jar"); + Files.remove(file); + Files.mkdirs(file.getParentFile()); + FileOutputStream to = new FileOutputStream(file); + IO.copy(resolver.resolve("mvn:http://repo1.maven.org/maven2/!junit:junit:LATEST:jar"), to); + IO.close(to); + assertTrue(file.exists()); + assertTrue(Collections.list(new JarFile(file).entries()).size() > 10 /* 323 */); // just check it is not an error page + } +}
