All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ops4j.pax.url.mvn.internal.Connection Maven / Gradle / Ivy

Go to download

OPS4J Pax Url - mvn: protocol handler. Detailed information to be found at http://wiki.ops4j.org/confluence/x/CoA6.

There is a newer version: 1.3.7
Show newest version
/*
 * Copyright 2007 Alin Dreghiciu.
 *
 * Licensed  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.ops4j.pax.url.mvn.internal;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import org.ops4j.lang.NullArgumentException;
import org.ops4j.net.URLUtils;
import org.ops4j.pax.url.maven.commons.MavenConfiguration;
import org.ops4j.pax.url.maven.commons.MavenRepositoryURL;
import org.ops4j.util.xml.XmlUtils;

/**
 * An URLConnextion that supports mvn: protocol.
* Syntax:
* mvn:[repository_url!]groupId/artifactId[/version[/type]]
* where:
* - repository_url = an url that points to a maven 2 repository; optional, if not sepecified the repositories are * resolved based on the repository/localRepository.
* - groupId = group id of maven artifact; mandatory
* - artifactId = artifact id of maven artifact; mandatory
* - version = version of maven artifact; optional, if not specified uses LATEST and will try to resolve the version * from available maven metadata. If version is a SNAPSHOT version, SNAPSHOT will be resolved from available maven * metadata
* - type = type of maven artifact; optional, if not specified uses JAR
* Examples:
* mvn:http://repository.ops4j.org/mvn-releases!org.ops4j.pax.runner/runner/0.4.0 - an artifact from an http repository
* mvn:http://user:[email protected]/mvn-releases!org.ops4j.pax.runner/runner/0.4.0 - an artifact from an http * repository with authentication
* mvn:file://c:/localRepo!org.ops4j.pax.runner/runner/0.4.0 - an artifact from a directory
* mvn:jar:file://c:/repo.zip!/repository!org.ops4j.pax.runner/runner/0.4.0 - an artifact from a zip file
* mvn:org.ops4j.pax.runner/runner/0.4.0 - an artifact that will be resolved based on the configured repositories
*
* The service can be configured in two ways: via configuration admin if available and via framework/system properties * where the configuration via config admin has priority.
* Service configuration:
* - org.ops4j.pax.url.mvn.settings = the path to settings.xml;
* - org.ops4j.pax.url.mvn.localRepository = the path to local repository directory;
* - org.ops4j.pax.url.mvn.repository = a comma separated list for repositories urls;
* - org.ops4j.pax.url.mvn.certicateCheck = true/false if the SSL certificate check should be done. * Default false. * * @author Alin Dreghiciu * @since August 10, 2007 */ public class Connection extends URLConnection { /** * Logger. */ private static final Log LOG = LogFactory.getLog( Connection.class ); /** * 2 spacess indent; */ private static final String Ix2 = " "; /** * 4 spacess indent; */ private static final String Ix4 = " "; /** * Parsed url. */ private Parser m_parser; /** * Service configuration. */ private final MavenConfiguration m_configuration; /** * Creates a new connection. * * @param url the url; cannot be null. * @param configuration service configuration; cannot be null * * @throws MalformedURLException in case of a malformed url */ public Connection( final URL url, final MavenConfiguration configuration ) throws MalformedURLException { super( url ); NullArgumentException.validateNotNull( url, "URL cannot be null" ); NullArgumentException.validateNotNull( configuration, "Service configuration" ); m_configuration = configuration; m_parser = new Parser( url.getPath() ); } /** * Does nothing. * * @see java.net.URLConnection#connect() */ @Override public void connect() { // do nothing } /** * Returns the input stream denoted by the url.
* If the url does not contain a repository the resource is searched in every repository if available, in the order * provided by the repository setting. * * @return the input stream for the resource denoted by url * * @throws IOException in case of an exception during accessing the resource * @see java.net.URLConnection#getInputStream() */ @Override public InputStream getInputStream() throws IOException { connect(); LOG.debug( "Resolving [" + url.toExternalForm() + "]" ); final Set defaultDownloadables = collectDefaultPossibleDownloads(); if( LOG.isTraceEnabled() ) { LOG.trace( "Possible default download locations for [" + url.toExternalForm() + "]" ); for( DownloadableArtifact artifact : defaultDownloadables ) { LOG.trace( " " + artifact ); } } for( DownloadableArtifact artifact : defaultDownloadables ) { LOG.trace( "Downloading [" + artifact + "]" ); try { m_configuration.enableProxy( artifact.getArtifactURL() ); return artifact.getInputStream(); } catch( IOException ignore ) { // go on with next repository LOG.debug( Ix2 + "Could not download [" + artifact + "]" ); LOG.trace( Ix2 + "Reason [" + ignore.getClass().getName() + ": " + ignore.getMessage() + "]" ); } } final Set downloadables = collectPossibleDownloads(); if( LOG.isTraceEnabled() ) { LOG.trace( "Possible download locations for [" + url.toExternalForm() + "]" ); for( DownloadableArtifact artifact : downloadables ) { LOG.trace( " " + artifact ); } } for( DownloadableArtifact artifact : downloadables ) { LOG.trace( "Downloading [" + artifact + "]" ); try { m_configuration.enableProxy( artifact.getArtifactURL() ); return artifact.getInputStream(); } catch( IOException ignore ) { // go on with next repository LOG.debug( Ix2 + "Could not download [" + artifact + "]" ); LOG.trace( Ix2 + "Reason [" + ignore.getClass().getName() + ": " + ignore.getMessage() + "]" ); } } // no artifact found throw new RuntimeException( "URL [" + url.toExternalForm() + "] could not be resolved." ); } /** * Searches all available repositories for possible artifacts to download. The returned set of downloadable * artifacts (never null, but maybe empty) will be sorted descending by version of the artifact and by positon of * repository in the list of repositories to be searched. * * @return a non null sorted set of artifacts * * @throws java.net.MalformedURLException re-thrown */ private Set collectPossibleDownloads() throws MalformedURLException { final List repositories = new ArrayList(); repositories.addAll( m_configuration.getRepositories() ); // if the url contains a prefered repository add that repository as the first repository to be searched if( m_parser.getRepositoryURL() != null ) { repositories.add( repositories.size() == 0 ? 0 : 1, m_parser.getRepositoryURL() ); } return doCollectPossibleDownloads( repositories ); } /** * Search the default repositories for possible artifacts to download. */ private Set collectDefaultPossibleDownloads() throws MalformedURLException { return doCollectPossibleDownloads( m_configuration.getDefaultRepositories() ); } private Set doCollectPossibleDownloads( final List repositories ) throws MalformedURLException { final Set downloadables = new TreeSet( new DownloadComparator() ); // find artifact type final boolean isLatest = m_parser.getVersion().contains( "LATEST" ); final boolean isSnapshot = m_parser.getVersion().endsWith( "SNAPSHOT" ); VersionRange versionRange = null; if( !isLatest && !isSnapshot ) { try { versionRange = new VersionRange( m_parser.getVersion() ); } catch( Exception ignore ) { // well, we do not have a range of versions } } final boolean isVersionRange = versionRange != null; final boolean isExactVersion = !( isLatest || isSnapshot || isVersionRange ); int priority = 0; for( MavenRepositoryURL repositoryURL : repositories ) { LOG.debug( "Collecting versions from repository [" + repositoryURL + "]" ); priority++; try { if( isExactVersion ) { downloadables.add( resolveExactVersion( repositoryURL, priority ) ); } else if( isSnapshot ) { final DownloadableArtifact snapshot = resolveSnapshotVersion( repositoryURL, priority, m_parser.getVersion() ); downloadables.add( snapshot ); // if we have a local built snapshot we skip the rest of repositories if( snapshot.isLocalSnapshotBuild() ) { break; } } else { final Document metadata = getMetadata( repositoryURL.getURL(), new String[] { m_parser.getArtifactLocalMetdataPath(), m_parser.getArtifactMetdataPath() } ); if( isLatest ) { downloadables.add( resolveLatestVersion( metadata, repositoryURL, priority ) ); } else { downloadables.addAll( resolveRangeVersions( metadata, repositoryURL, priority, versionRange ) ); } } } catch( IOException ignore ) { // if metadata cannot be found we go on with the next repository. Maybe we have better luck. LOG.debug( Ix2 + "Skipping repository [" + repositoryURL + "], reason: " + ignore.getMessage() ); } } return downloadables; } /** * Returns maven metadata by looking first for a local metatdata xml file and then for a remote one. * If no metadata file is found or cannot be used an IOException is thrown. * * @param repositoryURL url of the repository from where the metadata should be parsed * @param metadataLocations array of location paths to try as metadata * * @return parsed xml document for the metadata file * * @throws java.io.IOException if: * metadata file cannot be located */ private Document getMetadata( final URL repositoryURL, final String[] metadataLocations ) throws IOException { LOG.debug( Ix2 + "Resolving metadata" ); InputStream inputStream = null; String foundLocation = null; for( String location : metadataLocations ) { try { // first try to get the artifact local metadata inputStream = prepareInputStream( repositoryURL, location ); // get out at first found location foundLocation = location; LOG.trace( Ix4 + "Metadata found: [" + location + "]" ); break; } catch( IOException ignore ) { LOG.trace( Ix4 + "Metadata not found: [" + location + "]" ); } } if( inputStream == null ) { throw new IOException( "Metadata not found in repository [" + repositoryURL + "]" ); } try { return XmlUtils.parseDoc( inputStream ); } catch( ParserConfigurationException e ) { throw initIOException( "Metadata [" + foundLocation + "] could not be parsed.", e ); } catch( SAXException e ) { throw initIOException( "Metadata [" + foundLocation + "] could not be parsed.", e ); } } /** * Returns a downloadable artifact where the version is fully specified. * * @param repositoryURL the url of the repository to download from * @param priority repository priority * * @return a downloadable artifact * * @throws IOException re-thrown */ private DownloadableArtifact resolveExactVersion( final MavenRepositoryURL repositoryURL, final int priority ) throws IOException { if( !repositoryURL.isReleasesEnabled() ) { throw new IOException( "Releases not enabled" ); } LOG.debug( Ix2 + "Resolving exact version" ); return new DownloadableArtifact( m_parser.getVersion(), priority, repositoryURL.getURL(), m_parser.getArtifactPath(), false, // no local built snapshot m_configuration.getCertificateCheck() ); } /** * Resolves the latest version of the artifact. * * @param metadata parsed metadata xml * @param repositoryURL the url of the repository to download from * @param priority repository priority * * @return a downloadable artifact or throw an IOException if latest version cannot be determined. * * @throws IOException if the artifact could not be resolved */ private DownloadableArtifact resolveLatestVersion( final Document metadata, final MavenRepositoryURL repositoryURL, final int priority ) throws IOException { LOG.debug( Ix2 + "Resolving latest version" ); final String version = XmlUtils.getTextContentOfElement( metadata, "versioning/versions/version[last]" ); if( version != null ) { if( version.endsWith( "SNAPSHOT" ) ) { return resolveSnapshotVersion( repositoryURL, priority, version ); } else { return new DownloadableArtifact( version, priority, repositoryURL.getURL(), m_parser.getArtifactPath( version ), false, // no local built snapshot m_configuration.getCertificateCheck() ); } } throw new IOException( "LATEST version could not be resolved." ); } /** * Resolves snapshot version of the artifact. * Snapshot versions are resolved by parsing the metadata within the directory that contains the version as: * 1. if the metadata containes entries like "versioning/snapshot/timestamp (most likely on remote repos) it will * use the timestamp and buildnumber to point the real version * 2. if the metatdata does not contain the above (most likely a local repo) it will use as version the * versioning/lastUpdated * * @param repositoryURL the url of the repository to download from * @param priority repository priority * @param version snapshot version to resolve * * @return an input stream to the artifact * * @throws IOException if the artifact could not be resolved */ private DownloadableArtifact resolveSnapshotVersion( final MavenRepositoryURL repositoryURL, final int priority, final String version ) throws IOException { if( !repositoryURL.isSnapshotsEnabled() ) { throw new IOException( "Snapshots not enabled" ); } LOG.debug( Ix2 + "Resolving snapshot version [" + version + "]" ); try { final Document snapshotMetadata = getMetadata( repositoryURL.getURL(), new String[] { m_parser.getVersionLocalMetadataPath( version ), m_parser.getVersionMetadataPath( version ) } ); final String timestamp = XmlUtils.getTextContentOfElement( snapshotMetadata, "versioning/snapshot/timestamp" ); final String buildNumber = XmlUtils.getTextContentOfElement( snapshotMetadata, "versioning/snapshot/buildNumber" ); final String localSnapshot = XmlUtils.getTextContentOfElement( snapshotMetadata, "versioning/snapshot/localCopy" ); if( timestamp != null && buildNumber != null ) { return new DownloadableArtifact( m_parser.getSnapshotVersion( version, timestamp, buildNumber ), priority, repositoryURL.getURL(), m_parser.getSnapshotPath( version, timestamp, buildNumber ), localSnapshot != null, m_configuration.getCertificateCheck() ); } else { String lastUpdated = XmlUtils.getTextContentOfElement( snapshotMetadata, "versioning/lastUpdated" ); if( lastUpdated != null ) { // last updated should contain in the first 8 chars the date and then the time, // fact that is not compatible with timeStamp from remote repos which has a "." after date if( lastUpdated.length() > 8 ) { lastUpdated = lastUpdated.substring( 0, 8 ) + "." + lastUpdated.substring( 8 ); return new DownloadableArtifact( m_parser.getSnapshotVersion( version, lastUpdated, "0" ), priority, repositoryURL.getURL(), m_parser.getArtifactPath( version ), localSnapshot != null, m_configuration.getCertificateCheck() ); } } } } catch( IOException ignore ) { // in this case we could not find any metadata so try to get the *-SNAPSHOT file directly } return new DownloadableArtifact( m_parser.getVersion(), priority, repositoryURL.getURL(), m_parser.getArtifactPath(), false, // no local built snapshot m_configuration.getCertificateCheck() ); } /** * Resolves all versions that fits the provided range. * * @param metadata parsed metadata xml * @param repositoryURL the url of the repository to download from * @param priority repository priority * @param versionRange version range to fulfill * * @return list of downloadable artifacts that match the range * * @throws IOException re-thrown */ private List resolveRangeVersions( final Document metadata, final MavenRepositoryURL repositoryURL, final int priority, final VersionRange versionRange ) throws IOException { LOG.debug( Ix2 + "Resolving versions in range [" + versionRange + "]" ); final List downladables = new ArrayList(); final List elements = XmlUtils.getElements( metadata, "versioning/versions/version" ); if( elements != null && elements.size() > 0 ) { for( Element element : elements ) { final String versionString = XmlUtils.getTextContent( element ); if( versionString != null ) { final Version version = new Version( versionString ); if( versionRange.includes( version ) ) { if( versionString.endsWith( "SNAPSHOT" ) ) { downladables.add( resolveSnapshotVersion( repositoryURL, priority, versionString ) ); } else { downladables.add( new DownloadableArtifact( versionString, priority, repositoryURL.getURL(), m_parser.getArtifactPath( versionString ), false, // no local built snapshot m_configuration.getCertificateCheck() ) ); } } } } } return downladables; } /** * @param repositoryURL url to reporsitory * @param path a path to the artifact jar file * * @return prepared input stream * * @throws IOException re-thrown * @see org.ops4j.net.URLUtils#prepareInputStream(java.net.URL,boolean) */ private InputStream prepareInputStream( URL repositoryURL, final String path ) throws IOException { String repository = repositoryURL.toExternalForm(); if( !repository.endsWith( Parser.FILE_SEPARATOR ) ) { repository = repository + Parser.FILE_SEPARATOR; } m_configuration.enableProxy( repositoryURL ); final URL url = new URL( repository + path ); LOG.trace( "Reading " + url.toExternalForm() ); return URLUtils.prepareInputStream( url, !m_configuration.getCertificateCheck() ); } /** * Creates an IOException with a message and a cause. * * @param message exception message * @param cause exception cause * * @return the created IO Exception */ private IOException initIOException( final String message, final Exception cause ) { IOException exception = new IOException( message ); exception.initCause( cause ); return exception; } /** * Sorting comparator for downladable artifacts. * The sorting is done by: * 1. descending version * 2. ascending priority. */ private static class DownloadComparator implements Comparator { public int compare( final DownloadableArtifact first, final DownloadableArtifact second ) { // first descending by version int result = -1 * first.getVersion().compareTo( second.getVersion() ); if( result == 0 ) { // then ascending by priority if( first.getPriority() < second.getPriority() ) { result = -1; } else if( first.getPriority() > second.getPriority() ) { result = 1; } } return result; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy