You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by hb...@apache.org on 2016/09/03 20:23:25 UTC

[02/51] [partial] maven-aether git commit: [MNG-6007] rename Aether to Maven Artifact Resolver

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationContext.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationContext.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationContext.java
new file mode 100644
index 0000000..bd248a0
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationContext.java
@@ -0,0 +1,389 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import java.io.Closeable;
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.aether.RepositorySystemSession;
+
+/**
+ * A glorified map of key value pairs holding (cleartext) authentication data. Authentication contexts are used
+ * internally when network operations need to access secured repositories or proxies. Each authentication context
+ * manages the credentials required to access a single host. Unlike {@link Authentication} callbacks which exist for a
+ * potentially long time like the duration of a repository system session, an authentication context has a supposedly
+ * short lifetime and should be {@link #close() closed} as soon as the corresponding network operation has finished:
+ * 
+ * <pre>
+ * AuthenticationContext context = AuthenticationContext.forRepository( session, repository );
+ * try {
+ *     // get credentials
+ *     char[] password = context.get( AuthenticationContext.PASSWORD, char[].class );
+ *     // perform network operation using retrieved credentials
+ *     ...
+ * } finally {
+ *     // erase confidential authentication data from heap memory
+ *     AuthenticationContext.close( context );
+ * }
+ * </pre>
+ * 
+ * The same authentication data can often be presented using different data types, e.g. a password can be presented
+ * using a character array or (less securely) using a string. For ease of use, an authentication context treats the
+ * following groups of data types as equivalent and converts values automatically during retrieval:
+ * <ul>
+ * <li>{@code String}, {@code char[]}</li>
+ * <li>{@code String}, {@code File}</li>
+ * </ul>
+ * An authentication context is thread-safe.
+ */
+public final class AuthenticationContext
+    implements Closeable
+{
+
+    /**
+     * The key used to store the username. The corresponding authentication data should be of type {@link String}.
+     */
+    public static final String USERNAME = "username";
+
+    /**
+     * The key used to store the password. The corresponding authentication data should be of type {@code char[]} or
+     * {@link String}.
+     */
+    public static final String PASSWORD = "password";
+
+    /**
+     * The key used to store the NTLM domain. The corresponding authentication data should be of type {@link String}.
+     */
+    public static final String NTLM_DOMAIN = "ntlm.domain";
+
+    /**
+     * The key used to store the NTML workstation. The corresponding authentication data should be of type
+     * {@link String}.
+     */
+    public static final String NTLM_WORKSTATION = "ntlm.workstation";
+
+    /**
+     * The key used to store the pathname to a private key file. The corresponding authentication data should be of type
+     * {@link String} or {@link File}.
+     */
+    public static final String PRIVATE_KEY_PATH = "privateKey.path";
+
+    /**
+     * The key used to store the passphrase protecting the private key. The corresponding authentication data should be
+     * of type {@code char[]} or {@link String}.
+     */
+    public static final String PRIVATE_KEY_PASSPHRASE = "privateKey.passphrase";
+
+    /**
+     * The key used to store the acceptance policy for unknown host keys. The corresponding authentication data should
+     * be of type {@link Boolean}. When querying this authentication data, the extra data should provide
+     * {@link #HOST_KEY_REMOTE} and {@link #HOST_KEY_LOCAL}, e.g. to enable a well-founded decision of the user during
+     * an interactive prompt.
+     */
+    public static final String HOST_KEY_ACCEPTANCE = "hostKey.acceptance";
+
+    /**
+     * The key used to store the fingerprint of the public key advertised by remote host. Note that this key is used to
+     * query the extra data passed to {@link #get(String, Map, Class)} when getting {@link #HOST_KEY_ACCEPTANCE}, not
+     * the authentication data in a context.
+     */
+    public static final String HOST_KEY_REMOTE = "hostKey.remote";
+
+    /**
+     * The key used to store the fingerprint of the public key expected from remote host as recorded in a known hosts
+     * database. Note that this key is used to query the extra data passed to {@link #get(String, Map, Class)} when
+     * getting {@link #HOST_KEY_ACCEPTANCE}, not the authentication data in a context.
+     */
+    public static final String HOST_KEY_LOCAL = "hostKey.local";
+
+    /**
+     * The key used to store the SSL context. The corresponding authentication data should be of type
+     * {@link javax.net.ssl.SSLContext}.
+     */
+    public static final String SSL_CONTEXT = "ssl.context";
+
+    /**
+     * The key used to store the SSL hostname verifier. The corresponding authentication data should be of type
+     * {@link javax.net.ssl.HostnameVerifier}.
+     */
+    public static final String SSL_HOSTNAME_VERIFIER = "ssl.hostnameVerifier";
+
+    private final RepositorySystemSession session;
+
+    private final RemoteRepository repository;
+
+    private final Proxy proxy;
+
+    private final Authentication auth;
+
+    private final Map<String, Object> authData;
+
+    private boolean fillingAuthData;
+
+    /**
+     * Gets an authentication context for the specified repository.
+     * 
+     * @param session The repository system session during which the repository is accessed, must not be {@code null}.
+     * @param repository The repository for which to create an authentication context, must not be {@code null}.
+     * @return An authentication context for the repository or {@code null} if no authentication is configured for it.
+     */
+    public static AuthenticationContext forRepository( RepositorySystemSession session, RemoteRepository repository )
+    {
+        return newInstance( session, repository, null, repository.getAuthentication() );
+    }
+
+    /**
+     * Gets an authentication context for the proxy of the specified repository.
+     * 
+     * @param session The repository system session during which the repository is accessed, must not be {@code null}.
+     * @param repository The repository for whose proxy to create an authentication context, must not be {@code null}.
+     * @return An authentication context for the proxy or {@code null} if no proxy is set or no authentication is
+     *         configured for it.
+     */
+    public static AuthenticationContext forProxy( RepositorySystemSession session, RemoteRepository repository )
+    {
+        Proxy proxy = repository.getProxy();
+        return newInstance( session, repository, proxy, ( proxy != null ) ? proxy.getAuthentication() : null );
+    }
+
+    private static AuthenticationContext newInstance( RepositorySystemSession session, RemoteRepository repository,
+                                                      Proxy proxy, Authentication auth )
+    {
+        if ( auth == null )
+        {
+            return null;
+        }
+        return new AuthenticationContext( session, repository, proxy, auth );
+    }
+
+    private AuthenticationContext( RepositorySystemSession session, RemoteRepository repository, Proxy proxy,
+                                   Authentication auth )
+    {
+        if ( session == null )
+        {
+            throw new IllegalArgumentException( "repository system session missing" );
+        }
+        this.session = session;
+        this.repository = repository;
+        this.proxy = proxy;
+        this.auth = auth;
+        authData = new HashMap<String, Object>();
+    }
+
+    /**
+     * Gets the repository system session during which the authentication happens.
+     * 
+     * @return The repository system session, never {@code null}.
+     */
+    public RepositorySystemSession getSession()
+    {
+        return session;
+    }
+
+    /**
+     * Gets the repository requiring authentication. If {@link #getProxy()} is not {@code null}, the data gathered by
+     * this authentication context does not apply to the repository's host but rather the proxy.
+     * 
+     * @return The repository to be contacted, never {@code null}.
+     */
+    public RemoteRepository getRepository()
+    {
+        return repository;
+    }
+
+    /**
+     * Gets the proxy (if any) to be authenticated with.
+     * 
+     * @return The proxy or {@code null} if authenticating directly with the repository's host.
+     */
+    public Proxy getProxy()
+    {
+        return proxy;
+    }
+
+    /**
+     * Gets the authentication data for the specified key.
+     * 
+     * @param key The key whose authentication data should be retrieved, must not be {@code null}.
+     * @return The requested authentication data or {@code null} if none.
+     */
+    public String get( String key )
+    {
+        return get( key, null, String.class );
+    }
+
+    /**
+     * Gets the authentication data for the specified key.
+     * 
+     * @param <T> The data type of the authentication data.
+     * @param key The key whose authentication data should be retrieved, must not be {@code null}.
+     * @param type The expected type of the authentication data, must not be {@code null}.
+     * @return The requested authentication data or {@code null} if none or if the data doesn't match the expected type.
+     */
+    public <T> T get( String key, Class<T> type )
+    {
+        return get( key, null, type );
+    }
+
+    /**
+     * Gets the authentication data for the specified key.
+     * 
+     * @param <T> The data type of the authentication data.
+     * @param key The key whose authentication data should be retrieved, must not be {@code null}.
+     * @param data Any (read-only) extra data in form of key value pairs that might be useful when getting the
+     *            authentication data, may be {@code null}.
+     * @param type The expected type of the authentication data, must not be {@code null}.
+     * @return The requested authentication data or {@code null} if none or if the data doesn't match the expected type.
+     */
+    public <T> T get( String key, Map<String, String> data, Class<T> type )
+    {
+        if ( key == null )
+        {
+            throw new IllegalArgumentException( "authentication data key missing" );
+        }
+        Object value;
+        synchronized ( authData )
+        {
+            value = authData.get( key );
+            if ( value == null && !authData.containsKey( key ) && !fillingAuthData )
+            {
+                if ( auth != null )
+                {
+                    try
+                    {
+                        fillingAuthData = true;
+                        auth.fill( this, key, data );
+                    }
+                    finally
+                    {
+                        fillingAuthData = false;
+                    }
+                    value = authData.get( key );
+                }
+                if ( value == null )
+                {
+                    authData.put( key, value );
+                }
+            }
+        }
+
+        return convert( value, type );
+    }
+
+    private <T> T convert( Object value, Class<T> type )
+    {
+        if ( !type.isInstance( value ) )
+        {
+            if ( String.class.equals( type ) )
+            {
+                if ( value instanceof File )
+                {
+                    value = ( (File) value ).getPath();
+                }
+                else if ( value instanceof char[] )
+                {
+                    value = new String( (char[]) value );
+                }
+            }
+            else if ( File.class.equals( type ) )
+            {
+                if ( value instanceof String )
+                {
+                    value = new File( (String) value );
+                }
+            }
+            else if ( char[].class.equals( type ) )
+            {
+                if ( value instanceof String )
+                {
+                    value = ( (String) value ).toCharArray();
+                }
+            }
+        }
+
+        if ( type.isInstance( value ) )
+        {
+            return type.cast( value );
+        }
+
+        return null;
+    }
+
+    /**
+     * Puts the specified authentication data into this context. This method should only be called from implementors of
+     * {@link Authentication#fill(AuthenticationContext, String, Map)}. Passed in character arrays are not cloned and
+     * become owned by this context, i.e. get erased when the context gets closed.
+     * 
+     * @param key The key to associate the authentication data with, must not be {@code null}.
+     * @param value The (cleartext) authentication data to store, may be {@code null}.
+     */
+    public void put( String key, Object value )
+    {
+        if ( key == null )
+        {
+            throw new IllegalArgumentException( "authentication data key missing" );
+        }
+        synchronized ( authData )
+        {
+            Object oldValue = authData.put( key, value );
+            if ( oldValue instanceof char[] )
+            {
+                Arrays.fill( (char[]) oldValue, '\0' );
+            }
+        }
+    }
+
+    /**
+     * Closes this authentication context and erases sensitive authentication data from heap memory. Closing an already
+     * closed context has no effect.
+     */
+    public void close()
+    {
+        synchronized ( authData )
+        {
+            for ( Object value : authData.values() )
+            {
+                if ( value instanceof char[] )
+                {
+                    Arrays.fill( (char[]) value, '\0' );
+                }
+            }
+            authData.clear();
+        }
+    }
+
+    /**
+     * Closes the specified authentication context. This is a convenience method doing a {@code null} check before
+     * calling {@link #close()} on the given context.
+     * 
+     * @param context The authentication context to close, may be {@code null}.
+     */
+    public static void close( AuthenticationContext context )
+    {
+        if ( context != null )
+        {
+            context.close();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationDigest.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationDigest.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationDigest.java
new file mode 100644
index 0000000..03725b6
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationDigest.java
@@ -0,0 +1,219 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.eclipse.aether.RepositorySystemSession;
+
+/**
+ * A helper to calculate a fingerprint/digest for the authentication data of a repository/proxy. Such a fingerprint can
+ * be used to detect changes in the authentication data across JVM restarts without exposing sensitive information.
+ */
+public final class AuthenticationDigest
+{
+
+    private final MessageDigest digest;
+
+    private final RepositorySystemSession session;
+
+    private final RemoteRepository repository;
+
+    private final Proxy proxy;
+
+    /**
+     * Gets the fingerprint for the authentication of the specified repository.
+     * 
+     * @param session The repository system session during which the fingerprint is requested, must not be {@code null}.
+     * @param repository The repository whose authentication is to be fingerprinted, must not be {@code null}.
+     * @return The fingerprint of the repository authentication or an empty string if no authentication is configured,
+     *         never {@code null}.
+     */
+    public static String forRepository( RepositorySystemSession session, RemoteRepository repository )
+    {
+        String digest = "";
+        Authentication auth = repository.getAuthentication();
+        if ( auth != null )
+        {
+            AuthenticationDigest authDigest = new AuthenticationDigest( session, repository, null );
+            auth.digest( authDigest );
+            digest = authDigest.digest();
+        }
+        return digest;
+    }
+
+    /**
+     * Gets the fingerprint for the authentication of the specified repository's proxy.
+     * 
+     * @param session The repository system session during which the fingerprint is requested, must not be {@code null}.
+     * @param repository The repository whose proxy authentication is to be fingerprinted, must not be {@code null}.
+     * @return The fingerprint of the proxy authentication or an empty string if no proxy is present or if no proxy
+     *         authentication is configured, never {@code null}.
+     */
+    public static String forProxy( RepositorySystemSession session, RemoteRepository repository )
+    {
+        String digest = "";
+        Proxy proxy = repository.getProxy();
+        if ( proxy != null )
+        {
+            Authentication auth = proxy.getAuthentication();
+            if ( auth != null )
+            {
+                AuthenticationDigest authDigest = new AuthenticationDigest( session, repository, proxy );
+                auth.digest( authDigest );
+                digest = authDigest.digest();
+            }
+        }
+        return digest;
+    }
+
+    private AuthenticationDigest( RepositorySystemSession session, RemoteRepository repository, Proxy proxy )
+    {
+        this.session = session;
+        this.repository = repository;
+        this.proxy = proxy;
+        digest = newDigest();
+    }
+
+    private static MessageDigest newDigest()
+    {
+        try
+        {
+            return MessageDigest.getInstance( "SHA-1" );
+        }
+        catch ( NoSuchAlgorithmException e )
+        {
+            try
+            {
+                return MessageDigest.getInstance( "MD5" );
+            }
+            catch ( NoSuchAlgorithmException ne )
+            {
+                throw new IllegalStateException( ne );
+            }
+        }
+    }
+
+    /**
+     * Gets the repository system session during which the authentication fingerprint is calculated.
+     * 
+     * @return The repository system session, never {@code null}.
+     */
+    public RepositorySystemSession getSession()
+    {
+        return session;
+    }
+
+    /**
+     * Gets the repository requiring authentication. If {@link #getProxy()} is not {@code null}, the data gathered by
+     * this authentication digest does not apply to the repository's host but rather the proxy.
+     * 
+     * @return The repository to be contacted, never {@code null}.
+     */
+    public RemoteRepository getRepository()
+    {
+        return repository;
+    }
+
+    /**
+     * Gets the proxy (if any) to be authenticated with.
+     * 
+     * @return The proxy or {@code null} if authenticating directly with the repository's host.
+     */
+    public Proxy getProxy()
+    {
+        return proxy;
+    }
+
+    /**
+     * Updates the digest with the specified strings.
+     * 
+     * @param strings The strings to update the digest with, may be {@code null} or contain {@code null} elements.
+     */
+    public void update( String... strings )
+    {
+        if ( strings != null )
+        {
+            for ( String string : strings )
+            {
+                if ( string != null )
+                {
+                    try
+                    {
+                        digest.update( string.getBytes( "UTF-8" ) );
+                    }
+                    catch ( UnsupportedEncodingException e )
+                    {
+                        throw new IllegalStateException( e );
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Updates the digest with the specified characters.
+     * 
+     * @param chars The characters to update the digest with, may be {@code null}.
+     */
+    public void update( char... chars )
+    {
+        if ( chars != null )
+        {
+            for ( char c : chars )
+            {
+                digest.update( (byte) ( c >> 8 ) );
+                digest.update( (byte) ( c & 0xFF ) );
+            }
+        }
+    }
+
+    /**
+     * Updates the digest with the specified bytes.
+     * 
+     * @param bytes The bytes to update the digest with, may be {@code null}.
+     */
+    public void update( byte... bytes )
+    {
+        if ( bytes != null )
+        {
+            digest.update( bytes );
+        }
+    }
+
+    private String digest()
+    {
+        byte[] bytes = digest.digest();
+        StringBuilder buffer = new StringBuilder( bytes.length * 2 );
+        for ( byte aByte : bytes )
+        {
+            int b = aByte & 0xFF;
+            if ( b < 0x10 )
+            {
+                buffer.append( '0' );
+            }
+            buffer.append( Integer.toHexString( b ) );
+        }
+        return buffer.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationSelector.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationSelector.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationSelector.java
new file mode 100644
index 0000000..0637d1c
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/AuthenticationSelector.java
@@ -0,0 +1,38 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+/**
+ * Selects authentication for a given remote repository.
+ * 
+ * @see org.eclipse.aether.RepositorySystemSession#getAuthenticationSelector()
+ */
+public interface AuthenticationSelector
+{
+
+    /**
+     * Selects authentication for the specified remote repository.
+     * 
+     * @param repository The repository for which to select authentication, must not be {@code null}.
+     * @return The selected authentication or {@code null} if none.
+     */
+    Authentication getAuthentication( RemoteRepository repository );
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactRegistration.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactRegistration.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactRegistration.java
new file mode 100644
index 0000000..1065779
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactRegistration.java
@@ -0,0 +1,149 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+
+/**
+ * A request to register an artifact within the local repository. Certain local repository implementations can refuse to
+ * serve physically present artifacts if those haven't been previously registered to them.
+ * 
+ * @see LocalRepositoryManager#add(RepositorySystemSession, LocalArtifactRegistration)
+ */
+public final class LocalArtifactRegistration
+{
+
+    private Artifact artifact;
+
+    private RemoteRepository repository;
+
+    private Collection<String> contexts = Collections.emptyList();
+
+    /**
+     * Creates an uninitialized registration.
+     */
+    public LocalArtifactRegistration()
+    {
+        // enables default constructor
+    }
+
+    /**
+     * Creates a registration request for the specified (locally installed) artifact.
+     * 
+     * @param artifact The artifact to register, may be {@code null}.
+     */
+    public LocalArtifactRegistration( Artifact artifact )
+    {
+        setArtifact( artifact );
+    }
+
+    /**
+     * Creates a registration request for the specified artifact.
+     * 
+     * @param artifact The artifact to register, may be {@code null}.
+     * @param repository The remote repository from which the artifact was resolved or {@code null} if the artifact was
+     *            locally installed.
+     * @param contexts The resolution contexts, may be {@code null}.
+     */
+    public LocalArtifactRegistration( Artifact artifact, RemoteRepository repository, Collection<String> contexts )
+    {
+        setArtifact( artifact );
+        setRepository( repository );
+        setContexts( contexts );
+    }
+
+    /**
+     * Gets the artifact to register.
+     * 
+     * @return The artifact or {@code null} if not set.
+     */
+    public Artifact getArtifact()
+    {
+        return artifact;
+    }
+
+    /**
+     * Sets the artifact to register.
+     * 
+     * @param artifact The artifact, may be {@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public LocalArtifactRegistration setArtifact( Artifact artifact )
+    {
+        this.artifact = artifact;
+        return this;
+    }
+
+    /**
+     * Gets the remote repository from which the artifact was resolved.
+     * 
+     * @return The remote repository or {@code null} if the artifact was locally installed.
+     */
+    public RemoteRepository getRepository()
+    {
+        return repository;
+    }
+
+    /**
+     * Sets the remote repository from which the artifact was resolved.
+     * 
+     * @param repository The remote repository or {@code null} if the artifact was locally installed.
+     * @return This request for chaining, never {@code null}.
+     */
+    public LocalArtifactRegistration setRepository( RemoteRepository repository )
+    {
+        this.repository = repository;
+        return this;
+    }
+
+    /**
+     * Gets the resolution contexts in which the artifact is available.
+     * 
+     * @return The resolution contexts in which the artifact is available, never {@code null}.
+     */
+    public Collection<String> getContexts()
+    {
+        return contexts;
+    }
+
+    /**
+     * Sets the resolution contexts in which the artifact is available.
+     * 
+     * @param contexts The resolution contexts, may be {@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public LocalArtifactRegistration setContexts( Collection<String> contexts )
+    {
+        if ( contexts != null )
+        {
+            this.contexts = contexts;
+        }
+        else
+        {
+            this.contexts = Collections.emptyList();
+        }
+        return this;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactRequest.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactRequest.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactRequest.java
new file mode 100644
index 0000000..8f6eabf
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactRequest.java
@@ -0,0 +1,145 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+
+/**
+ * A query to the local repository for the existence of an artifact.
+ * 
+ * @see LocalRepositoryManager#find(RepositorySystemSession, LocalArtifactRequest)
+ */
+public final class LocalArtifactRequest
+{
+
+    private Artifact artifact;
+
+    private String context = "";
+
+    private List<RemoteRepository> repositories = Collections.emptyList();
+
+    /**
+     * Creates an uninitialized query.
+     */
+    public LocalArtifactRequest()
+    {
+        // enables default constructor
+    }
+
+    /**
+     * Creates a query with the specified properties.
+     * 
+     * @param artifact The artifact to query for, may be {@code null}.
+     * @param repositories The remote repositories that should be considered as potential sources for the artifact, may
+     *            be {@code null} or empty to only consider locally installed artifacts.
+     * @param context The resolution context for the artifact, may be {@code null}.
+     */
+    public LocalArtifactRequest( Artifact artifact, List<RemoteRepository> repositories, String context )
+    {
+        setArtifact( artifact );
+        setRepositories( repositories );
+        setContext( context );
+    }
+
+    /**
+     * Gets the artifact to query for.
+     * 
+     * @return The artifact or {@code null} if not set.
+     */
+    public Artifact getArtifact()
+    {
+        return artifact;
+    }
+
+    /**
+     * Sets the artifact to query for.
+     * 
+     * @param artifact The artifact, may be {@code null}.
+     * @return This query for chaining, never {@code null}.
+     */
+    public LocalArtifactRequest setArtifact( Artifact artifact )
+    {
+        this.artifact = artifact;
+        return this;
+    }
+
+    /**
+     * Gets the resolution context.
+     * 
+     * @return The resolution context, never {@code null}.
+     */
+    public String getContext()
+    {
+        return context;
+    }
+
+    /**
+     * Sets the resolution context.
+     * 
+     * @param context The resolution context, may be {@code null}.
+     * @return This query for chaining, never {@code null}.
+     */
+    public LocalArtifactRequest setContext( String context )
+    {
+        this.context = ( context != null ) ? context : "";
+        return this;
+    }
+
+    /**
+     * Gets the remote repositories to consider as sources of the artifact.
+     * 
+     * @return The remote repositories, never {@code null}.
+     */
+    public List<RemoteRepository> getRepositories()
+    {
+        return repositories;
+    }
+
+    /**
+     * Sets the remote repositories to consider as sources of the artifact.
+     * 
+     * @param repositories The remote repositories, may be {@code null} or empty to only consider locally installed
+     *            artifacts.
+     * @return This query for chaining, never {@code null}.
+     */
+    public LocalArtifactRequest setRepositories( List<RemoteRepository> repositories )
+    {
+        if ( repositories != null )
+        {
+            this.repositories = repositories;
+        }
+        else
+        {
+            this.repositories = Collections.emptyList();
+        }
+        return this;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getArtifact() + " @ " + getRepositories();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactResult.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactResult.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactResult.java
new file mode 100644
index 0000000..e611b27
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalArtifactResult.java
@@ -0,0 +1,147 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import java.io.File;
+
+import org.eclipse.aether.RepositorySystemSession;
+
+/**
+ * A result from the local repository about the existence of an artifact.
+ * 
+ * @see LocalRepositoryManager#find(RepositorySystemSession, LocalArtifactRequest)
+ */
+public final class LocalArtifactResult
+{
+
+    private final LocalArtifactRequest request;
+
+    private File file;
+
+    private boolean available;
+
+    private RemoteRepository repository;
+
+    /**
+     * Creates a new result for the specified request.
+     * 
+     * @param request The local artifact request, must not be {@code null}.
+     */
+    public LocalArtifactResult( LocalArtifactRequest request )
+    {
+        if ( request == null )
+        {
+            throw new IllegalArgumentException( "local artifact request has not been specified" );
+        }
+        this.request = request;
+    }
+
+    /**
+     * Gets the request corresponding to this result.
+     * 
+     * @return The corresponding request, never {@code null}.
+     */
+    public LocalArtifactRequest getRequest()
+    {
+        return request;
+    }
+
+    /**
+     * Gets the file to the requested artifact. Note that this file must not be used unless {@link #isAvailable()}
+     * returns {@code true}. An artifact file can be found but considered unavailable if the artifact was cached from a
+     * remote repository that is not part of the list of remote repositories used for the query.
+     * 
+     * @return The file to the requested artifact or {@code null} if the artifact does not exist locally.
+     */
+    public File getFile()
+    {
+        return file;
+    }
+
+    /**
+     * Sets the file to requested artifact.
+     * 
+     * @param file The artifact file, may be {@code null}.
+     * @return This result for chaining, never {@code null}.
+     */
+    public LocalArtifactResult setFile( File file )
+    {
+        this.file = file;
+        return this;
+    }
+
+    /**
+     * Indicates whether the requested artifact is available for use. As a minimum, the file needs to be physically
+     * existent in the local repository to be available. Additionally, a local repository manager can consider the list
+     * of supplied remote repositories to determine whether the artifact is logically available and mark an artifact
+     * unavailable (despite its physical existence) if it is not known to be hosted by any of the provided repositories.
+     * 
+     * @return {@code true} if the artifact is available, {@code false} otherwise.
+     * @see LocalArtifactRequest#getRepositories()
+     */
+    public boolean isAvailable()
+    {
+        return available;
+    }
+
+    /**
+     * Sets whether the artifact is available.
+     * 
+     * @param available {@code true} if the artifact is available, {@code false} otherwise.
+     * @return This result for chaining, never {@code null}.
+     */
+    public LocalArtifactResult setAvailable( boolean available )
+    {
+        this.available = available;
+        return this;
+    }
+
+    /**
+     * Gets the (first) remote repository from which the artifact was cached (if any).
+     * 
+     * @return The remote repository from which the artifact was originally retrieved or {@code null} if unknown or if
+     *         the artifact has been locally installed.
+     * @see LocalArtifactRequest#getRepositories()
+     */
+    public RemoteRepository getRepository()
+    {
+        return repository;
+    }
+
+    /**
+     * Sets the (first) remote repository from which the artifact was cached.
+     * 
+     * @param repository The remote repository from which the artifact was originally retrieved, may be {@code null} if
+     *            unknown or if the artifact has been locally installed.
+     * @return This result for chaining, never {@code null}.
+     */
+    public LocalArtifactResult setRepository( RemoteRepository repository )
+    {
+        this.repository = repository;
+        return this;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getFile() + " (" + ( isAvailable() ? "available" : "unavailable" ) + ")";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataRegistration.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataRegistration.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataRegistration.java
new file mode 100644
index 0000000..dd0d587
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataRegistration.java
@@ -0,0 +1,148 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.metadata.Metadata;
+
+/**
+ * A request to register metadata within the local repository.
+ * 
+ * @see LocalRepositoryManager#add(RepositorySystemSession, LocalMetadataRegistration)
+ */
+public final class LocalMetadataRegistration
+{
+
+    private Metadata metadata;
+
+    private RemoteRepository repository;
+
+    private Collection<String> contexts = Collections.emptyList();
+
+    /**
+     * Creates an uninitialized registration.
+     */
+    public LocalMetadataRegistration()
+    {
+        // enables default constructor
+    }
+
+    /**
+     * Creates a registration request for the specified metadata accompanying a locally installed artifact.
+     * 
+     * @param metadata The metadata to register, may be {@code null}.
+     */
+    public LocalMetadataRegistration( Metadata metadata )
+    {
+        setMetadata( metadata );
+    }
+
+    /**
+     * Creates a registration request for the specified metadata.
+     * 
+     * @param metadata The metadata to register, may be {@code null}.
+     * @param repository The remote repository from which the metadata was resolved or {@code null} if the metadata
+     *            accompanies a locally installed artifact.
+     * @param contexts The resolution contexts, may be {@code null}.
+     */
+    public LocalMetadataRegistration( Metadata metadata, RemoteRepository repository, Collection<String> contexts )
+    {
+        setMetadata( metadata );
+        setRepository( repository );
+        setContexts( contexts );
+    }
+
+    /**
+     * Gets the metadata to register.
+     * 
+     * @return The metadata or {@code null} if not set.
+     */
+    public Metadata getMetadata()
+    {
+        return metadata;
+    }
+
+    /**
+     * Sets the metadata to register.
+     * 
+     * @param metadata The metadata, may be {@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public LocalMetadataRegistration setMetadata( Metadata metadata )
+    {
+        this.metadata = metadata;
+        return this;
+    }
+
+    /**
+     * Gets the remote repository from which the metadata was resolved.
+     * 
+     * @return The remote repository or {@code null} if the metadata was locally installed.
+     */
+    public RemoteRepository getRepository()
+    {
+        return repository;
+    }
+
+    /**
+     * Sets the remote repository from which the metadata was resolved.
+     * 
+     * @param repository The remote repository or {@code null} if the metadata accompanies a locally installed artifact.
+     * @return This request for chaining, never {@code null}.
+     */
+    public LocalMetadataRegistration setRepository( RemoteRepository repository )
+    {
+        this.repository = repository;
+        return this;
+    }
+
+    /**
+     * Gets the resolution contexts in which the metadata is available.
+     * 
+     * @return The resolution contexts in which the metadata is available, never {@code null}.
+     */
+    public Collection<String> getContexts()
+    {
+        return contexts;
+    }
+
+    /**
+     * Sets the resolution contexts in which the metadata is available.
+     * 
+     * @param contexts The resolution contexts, may be {@code null}.
+     * @return This request for chaining, never {@code null}.
+     */
+    public LocalMetadataRegistration setContexts( Collection<String> contexts )
+    {
+        if ( contexts != null )
+        {
+            this.contexts = contexts;
+        }
+        else
+        {
+            this.contexts = Collections.emptyList();
+        }
+        return this;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataRequest.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataRequest.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataRequest.java
new file mode 100644
index 0000000..4c8f270
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataRequest.java
@@ -0,0 +1,133 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.metadata.Metadata;
+
+/**
+ * A query to the local repository for the existence of metadata.
+ * 
+ * @see LocalRepositoryManager#find(RepositorySystemSession, LocalMetadataRequest)
+ */
+public final class LocalMetadataRequest
+{
+
+    private Metadata metadata;
+
+    private String context = "";
+
+    private RemoteRepository repository = null;
+
+    /**
+     * Creates an uninitialized query.
+     */
+    public LocalMetadataRequest()
+    {
+        // enables default constructor
+    }
+
+    /**
+     * Creates a query with the specified properties.
+     * 
+     * @param metadata The metadata to query for, may be {@code null}.
+     * @param repository The source remote repository for the metadata, may be {@code null} for local metadata.
+     * @param context The resolution context for the metadata, may be {@code null}.
+     */
+    public LocalMetadataRequest( Metadata metadata, RemoteRepository repository, String context )
+    {
+        setMetadata( metadata );
+        setRepository( repository );
+        setContext( context );
+    }
+
+    /**
+     * Gets the metadata to query for.
+     * 
+     * @return The metadata or {@code null} if not set.
+     */
+    public Metadata getMetadata()
+    {
+        return metadata;
+    }
+
+    /**
+     * Sets the metadata to query for.
+     * 
+     * @param metadata The metadata, may be {@code null}.
+     * @return This query for chaining, never {@code null}.
+     */
+    public LocalMetadataRequest setMetadata( Metadata metadata )
+    {
+        this.metadata = metadata;
+        return this;
+    }
+
+    /**
+     * Gets the resolution context.
+     * 
+     * @return The resolution context, never {@code null}.
+     */
+    public String getContext()
+    {
+        return context;
+    }
+
+    /**
+     * Sets the resolution context.
+     * 
+     * @param context The resolution context, may be {@code null}.
+     * @return This query for chaining, never {@code null}.
+     */
+    public LocalMetadataRequest setContext( String context )
+    {
+        this.context = ( context != null ) ? context : "";
+        return this;
+    }
+
+    /**
+     * Gets the remote repository to use as source of the metadata.
+     * 
+     * @return The remote repositories, may be {@code null} for local metadata.
+     */
+    public RemoteRepository getRepository()
+    {
+        return repository;
+    }
+
+    /**
+     * Sets the remote repository to use as sources of the metadata.
+     * 
+     * @param repository The remote repository, may be {@code null}.
+     * @return This query for chaining, may be {@code null} for local metadata.
+     */
+    public LocalMetadataRequest setRepository( RemoteRepository repository )
+    {
+        this.repository = repository;
+        return this;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getMetadata() + " @ " + getRepository();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataResult.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataResult.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataResult.java
new file mode 100644
index 0000000..29bd045
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalMetadataResult.java
@@ -0,0 +1,114 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import java.io.File;
+
+import org.eclipse.aether.RepositorySystemSession;
+
+/**
+ * A result from the local repository about the existence of metadata.
+ * 
+ * @see LocalRepositoryManager#find(RepositorySystemSession, LocalMetadataRequest)
+ */
+public final class LocalMetadataResult
+{
+
+    private final LocalMetadataRequest request;
+
+    private File file;
+
+    private boolean stale;
+
+    /**
+     * Creates a new result for the specified request.
+     * 
+     * @param request The local metadata request, must not be {@code null}.
+     */
+    public LocalMetadataResult( LocalMetadataRequest request )
+    {
+        if ( request == null )
+        {
+            throw new IllegalArgumentException( "local metadata request has not been specified" );
+        }
+        this.request = request;
+    }
+
+    /**
+     * Gets the request corresponding to this result.
+     * 
+     * @return The corresponding request, never {@code null}.
+     */
+    public LocalMetadataRequest getRequest()
+    {
+        return request;
+    }
+
+    /**
+     * Gets the file to the requested metadata if the metadata is available in the local repository.
+     * 
+     * @return The file to the requested metadata or {@code null}.
+     */
+    public File getFile()
+    {
+        return file;
+    }
+
+    /**
+     * Sets the file to requested metadata.
+     * 
+     * @param file The metadata file, may be {@code null}.
+     * @return This result for chaining, never {@code null}.
+     */
+    public LocalMetadataResult setFile( File file )
+    {
+        this.file = file;
+        return this;
+    }
+
+    /**
+     * This value indicates whether the metadata is stale and should be updated.
+     * 
+     * @return {@code true} if the metadata is stale and should be updated, {@code false} otherwise.
+     */
+    public boolean isStale()
+    {
+        return stale;
+    }
+
+    /**
+     * Sets whether the metadata is stale.
+     * 
+     * @param stale {@code true} if the metadata is stale and should be updated, {@code false} otherwise.
+     * @return This result for chaining, never {@code null}.
+     */
+    public LocalMetadataResult setStale( boolean stale )
+    {
+        this.stale = stale;
+        return this;
+    }
+
+    @Override
+    public String toString()
+    {
+        return request.toString() + "(" + getFile() + ")";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java
new file mode 100644
index 0000000..32dce73
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java
@@ -0,0 +1,132 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import java.io.File;
+
+/**
+ * A repository on the local file system used to cache contents of remote repositories and to store locally installed
+ * artifacts. Note that this class merely describes such a repository, actual access to the contained artifacts is
+ * handled by a {@link LocalRepositoryManager} which is usually determined from the {@link #getContentType() type} of
+ * the repository.
+ */
+public final class LocalRepository
+    implements ArtifactRepository
+{
+
+    private final File basedir;
+
+    private final String type;
+
+    /**
+     * Creates a new local repository with the specified base directory and unknown type.
+     * 
+     * @param basedir The base directory of the repository, may be {@code null}.
+     */
+    public LocalRepository( String basedir )
+    {
+        this( ( basedir != null ) ? new File( basedir ) : null, "" );
+    }
+
+    /**
+     * Creates a new local repository with the specified base directory and unknown type.
+     * 
+     * @param basedir The base directory of the repository, may be {@code null}.
+     */
+    public LocalRepository( File basedir )
+    {
+        this( basedir, "" );
+    }
+
+    /**
+     * Creates a new local repository with the specified properties.
+     * 
+     * @param basedir The base directory of the repository, may be {@code null}.
+     * @param type The type of the repository, may be {@code null}.
+     */
+    public LocalRepository( File basedir, String type )
+    {
+        this.basedir = basedir;
+        this.type = ( type != null ) ? type : "";
+    }
+
+    public String getContentType()
+    {
+        return type;
+    }
+
+    public String getId()
+    {
+        return "local";
+    }
+
+    /**
+     * Gets the base directory of the repository.
+     * 
+     * @return The base directory or {@code null} if none.
+     */
+    public File getBasedir()
+    {
+        return basedir;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getBasedir() + " (" + getContentType() + ")";
+    }
+
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( this == obj )
+        {
+            return true;
+        }
+        if ( obj == null || !getClass().equals( obj.getClass() ) )
+        {
+            return false;
+        }
+
+        LocalRepository that = (LocalRepository) obj;
+
+        return eq( basedir, that.basedir ) && eq( type, that.type );
+    }
+
+    private static <T> boolean eq( T s1, T s2 )
+    {
+        return s1 != null ? s1.equals( s2 ) : s2 == null;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int hash = 17;
+        hash = hash * 31 + hash( basedir );
+        hash = hash * 31 + hash( type );
+        return hash;
+    }
+
+    private static int hash( Object obj )
+    {
+        return obj != null ? obj.hashCode() : 0;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepositoryManager.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepositoryManager.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepositoryManager.java
new file mode 100644
index 0000000..649707c
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepositoryManager.java
@@ -0,0 +1,127 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.metadata.Metadata;
+
+/**
+ * Manages access to a local repository.
+ * 
+ * @see RepositorySystemSession#getLocalRepositoryManager()
+ * @see org.eclipse.aether.RepositorySystem#newLocalRepositoryManager(RepositorySystemSession, LocalRepository)
+ */
+public interface LocalRepositoryManager
+{
+
+    /**
+     * Gets the description of the local repository being managed.
+     * 
+     * @return The description of the local repository, never {@code null}.
+     */
+    LocalRepository getRepository();
+
+    /**
+     * Gets the relative path for a locally installed artifact. Note that the artifact need not actually exist yet at
+     * the returned location, the path merely indicates where the artifact would eventually be stored. The path uses the
+     * forward slash as directory separator regardless of the underlying file system.
+     * 
+     * @param artifact The artifact for which to determine the path, must not be {@code null}.
+     * @return The path, relative to the local repository's base directory.
+     */
+    String getPathForLocalArtifact( Artifact artifact );
+
+    /**
+     * Gets the relative path for an artifact cached from a remote repository. Note that the artifact need not actually
+     * exist yet at the returned location, the path merely indicates where the artifact would eventually be stored. The
+     * path uses the forward slash as directory separator regardless of the underlying file system.
+     * 
+     * @param artifact The artifact for which to determine the path, must not be {@code null}.
+     * @param repository The source repository of the artifact, must not be {@code null}.
+     * @param context The resolution context in which the artifact is being requested, may be {@code null}.
+     * @return The path, relative to the local repository's base directory.
+     */
+    String getPathForRemoteArtifact( Artifact artifact, RemoteRepository repository, String context );
+
+    /**
+     * Gets the relative path for locally installed metadata. Note that the metadata need not actually exist yet at the
+     * returned location, the path merely indicates where the metadata would eventually be stored. The path uses the
+     * forward slash as directory separator regardless of the underlying file system.
+     * 
+     * @param metadata The metadata for which to determine the path, must not be {@code null}.
+     * @return The path, relative to the local repository's base directory.
+     */
+    String getPathForLocalMetadata( Metadata metadata );
+
+    /**
+     * Gets the relative path for metadata cached from a remote repository. Note that the metadata need not actually
+     * exist yet at the returned location, the path merely indicates where the metadata would eventually be stored. The
+     * path uses the forward slash as directory separator regardless of the underlying file system.
+     * 
+     * @param metadata The metadata for which to determine the path, must not be {@code null}.
+     * @param repository The source repository of the metadata, must not be {@code null}.
+     * @param context The resolution context in which the metadata is being requested, may be {@code null}.
+     * @return The path, relative to the local repository's base directory.
+     */
+    String getPathForRemoteMetadata( Metadata metadata, RemoteRepository repository, String context );
+
+    /**
+     * Queries for the existence of an artifact in the local repository. The request could be satisfied by a locally
+     * installed artifact or a previously downloaded artifact.
+     * 
+     * @param session The repository system session during which the request is made, must not be {@code null}.
+     * @param request The artifact request, must not be {@code null}.
+     * @return The result of the request, never {@code null}.
+     */
+    LocalArtifactResult find( RepositorySystemSession session, LocalArtifactRequest request );
+
+    /**
+     * Registers an installed or resolved artifact with the local repository. Note that artifact registration is merely
+     * concerned about updating the local repository's internal state, not about actually installing the artifact or its
+     * accompanying metadata.
+     * 
+     * @param session The repository system session during which the registration is made, must not be {@code null}.
+     * @param request The registration request, must not be {@code null}.
+     */
+    void add( RepositorySystemSession session, LocalArtifactRegistration request );
+
+    /**
+     * Queries for the existence of metadata in the local repository. The request could be satisfied by locally
+     * installed or previously downloaded metadata.
+     * 
+     * @param session The repository system session during which the request is made, must not be {@code null}.
+     * @param request The metadata request, must not be {@code null}.
+     * @return The result of the request, never {@code null}.
+     */
+    LocalMetadataResult find( RepositorySystemSession session, LocalMetadataRequest request );
+
+    /**
+     * Registers installed or resolved metadata with the local repository. Note that metadata registration is merely
+     * concerned about updating the local repository's internal state, not about actually installing the metadata.
+     * However, this method MUST be called after the actual install to give the repository manager the opportunity to
+     * inspect the added metadata.
+     * 
+     * @param session The repository system session during which the registration is made, must not be {@code null}.
+     * @param request The registration request, must not be {@code null}.
+     */
+    void add( RepositorySystemSession session, LocalMetadataRegistration request );
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/MirrorSelector.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/MirrorSelector.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/MirrorSelector.java
new file mode 100644
index 0000000..d50262c
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/MirrorSelector.java
@@ -0,0 +1,39 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+/**
+ * Selects a mirror for a given remote repository.
+ * 
+ * @see org.eclipse.aether.RepositorySystemSession#getMirrorSelector()
+ */
+public interface MirrorSelector
+{
+
+    /**
+     * Selects a mirror for the specified repository.
+     * 
+     * @param repository The repository to select a mirror for, must not be {@code null}.
+     * @return The selected mirror or {@code null} if none.
+     * @see RemoteRepository#getMirroredRepositories()
+     */
+    RemoteRepository getMirror( RemoteRepository repository );
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/NoLocalRepositoryManagerException.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/NoLocalRepositoryManagerException.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/NoLocalRepositoryManagerException.java
new file mode 100644
index 0000000..c804821
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/NoLocalRepositoryManagerException.java
@@ -0,0 +1,102 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+import org.eclipse.aether.RepositoryException;
+
+/**
+ * Thrown in case of an unsupported local repository type.
+ */
+public class NoLocalRepositoryManagerException
+    extends RepositoryException
+{
+
+    private final transient LocalRepository repository;
+
+    /**
+     * Creates a new exception with the specified repository.
+     * 
+     * @param repository The local repository for which no support is available, may be {@code null}.
+     */
+    public NoLocalRepositoryManagerException( LocalRepository repository )
+    {
+        this( repository, toMessage( repository ) );
+    }
+
+    /**
+     * Creates a new exception with the specified repository and detail message.
+     * 
+     * @param repository The local repository for which no support is available, may be {@code null}.
+     * @param message The detail message, may be {@code null}.
+     */
+    public NoLocalRepositoryManagerException( LocalRepository repository, String message )
+    {
+        super( message );
+        this.repository = repository;
+    }
+
+    /**
+     * Creates a new exception with the specified repository and cause.
+     * 
+     * @param repository The local repository for which no support is available, may be {@code null}.
+     * @param cause The exception that caused this one, may be {@code null}.
+     */
+    public NoLocalRepositoryManagerException( LocalRepository repository, Throwable cause )
+    {
+        this( repository, toMessage( repository ), cause );
+    }
+
+    /**
+     * Creates a new exception with the specified repository, detail message and cause.
+     * 
+     * @param repository The local repository for which no support is available, may be {@code null}.
+     * @param message The detail message, may be {@code null}.
+     * @param cause The exception that caused this one, may be {@code null}.
+     */
+    public NoLocalRepositoryManagerException( LocalRepository repository, String message, Throwable cause )
+    {
+        super( message, cause );
+        this.repository = repository;
+    }
+
+    private static String toMessage( LocalRepository repository )
+    {
+        if ( repository != null )
+        {
+            return "No manager available for local repository (" + repository.getBasedir().getAbsolutePath()
+                + ") of type " + repository.getContentType();
+        }
+        else
+        {
+            return "No manager available for local repository";
+        }
+    }
+
+    /**
+     * Gets the local repository whose content type is not supported.
+     * 
+     * @return The unsupported local repository or {@code null} if unknown.
+     */
+    public LocalRepository getRepository()
+    {
+        return repository;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/Proxy.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/Proxy.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/Proxy.java
new file mode 100644
index 0000000..8e8cba1
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/Proxy.java
@@ -0,0 +1,158 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+/**
+ * A proxy to use for connections to a repository.
+ */
+public final class Proxy
+{
+
+    /**
+     * Type denoting a proxy for HTTP transfers.
+     */
+    public static final String TYPE_HTTP = "http";
+
+    /**
+     * Type denoting a proxy for HTTPS transfers.
+     */
+    public static final String TYPE_HTTPS = "https";
+
+    private final String type;
+
+    private final String host;
+
+    private final int port;
+
+    private final Authentication auth;
+
+    /**
+     * Creates a new proxy with the specified properties and no authentication.
+     * 
+     * @param type The type of the proxy, e.g. "http", may be {@code null}.
+     * @param host The host of the proxy, may be {@code null}.
+     * @param port The port of the proxy.
+     */
+    public Proxy( String type, String host, int port )
+    {
+        this( type, host, port, null );
+    }
+
+    /**
+     * Creates a new proxy with the specified properties.
+     * 
+     * @param type The type of the proxy, e.g. "http", may be {@code null}.
+     * @param host The host of the proxy, may be {@code null}.
+     * @param port The port of the proxy.
+     * @param auth The authentication to use for the proxy connection, may be {@code null}.
+     */
+    public Proxy( String type, String host, int port, Authentication auth )
+    {
+        this.type = ( type != null ) ? type : "";
+        this.host = ( host != null ) ? host : "";
+        this.port = port;
+        this.auth = auth;
+    }
+
+    /**
+     * Gets the type of this proxy.
+     * 
+     * @return The type of this proxy, never {@code null}.
+     */
+    public String getType()
+    {
+        return type;
+    }
+
+    /**
+     * Gets the host for this proxy.
+     * 
+     * @return The host for this proxy, never {@code null}.
+     */
+    public String getHost()
+    {
+        return host;
+    }
+
+    /**
+     * Gets the port number for this proxy.
+     * 
+     * @return The port number for this proxy.
+     */
+    public int getPort()
+    {
+        return port;
+    }
+
+    /**
+     * Gets the authentication to use for the proxy connection.
+     * 
+     * @return The authentication to use or {@code null} if none.
+     */
+    public Authentication getAuthentication()
+    {
+        return auth;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getHost() + ':' + getPort();
+    }
+
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( this == obj )
+        {
+            return true;
+        }
+        if ( obj == null || !getClass().equals( obj.getClass() ) )
+        {
+            return false;
+        }
+
+        Proxy that = (Proxy) obj;
+
+        return eq( type, that.type ) && eq( host, that.host ) && port == that.port && eq( auth, that.auth );
+    }
+
+    private static <T> boolean eq( T s1, T s2 )
+    {
+        return s1 != null ? s1.equals( s2 ) : s2 == null;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int hash = 17;
+        hash = hash * 31 + hash( host );
+        hash = hash * 31 + hash( type );
+        hash = hash * 31 + port;
+        hash = hash * 31 + hash( auth );
+        return hash;
+    }
+
+    private static int hash( Object obj )
+    {
+        return obj != null ? obj.hashCode() : 0;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-aether/blob/27f8bd73/maven-resolver-api/src/main/java/org/eclipse/aether/repository/ProxySelector.java
----------------------------------------------------------------------
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/ProxySelector.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/ProxySelector.java
new file mode 100644
index 0000000..29b9e4e
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/ProxySelector.java
@@ -0,0 +1,38 @@
+package org.eclipse.aether.repository;
+
+/*
+ * 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.
+ */
+
+/**
+ * Selects a proxy for a given remote repository.
+ * 
+ * @see org.eclipse.aether.RepositorySystemSession#getProxySelector()
+ */
+public interface ProxySelector
+{
+
+    /**
+     * Selects a proxy for the specified remote repository.
+     * 
+     * @param repository The repository for which to select a proxy, must not be {@code null}.
+     * @return The selected proxy or {@code null} if none.
+     */
+    Proxy getProxy( RemoteRepository repository );
+
+}