You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ja...@apache.org on 2012/04/23 14:15:06 UTC

svn commit: r1329199 [2/5] - in /ace/trunk: ./ ace-authenticationprocessor-basicauth/src/main/java/org/apache/ace/authenticationprocessor/basicauth/ ace-client-automation/src/main/java/org/apache/ace/client/automation/ ace-client-repository-api/src/mai...

Modified: ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/impl/RepositoryAdminImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/impl/RepositoryAdminImpl.java?rev=1329199&r1=1329198&r2=1329199&view=diff
==============================================================================
--- ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/impl/RepositoryAdminImpl.java (original)
+++ ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/impl/RepositoryAdminImpl.java Mon Apr 23 12:15:01 2012
@@ -33,26 +33,27 @@ import org.apache.ace.client.repository.
 import org.apache.ace.client.repository.RepositoryAdmin;
 import org.apache.ace.client.repository.RepositoryAdminLoginContext;
 import org.apache.ace.client.repository.RepositoryObject;
-import org.apache.ace.client.repository.SessionFactory;
 import org.apache.ace.client.repository.RepositoryObject.WorkingState;
+import org.apache.ace.client.repository.SessionFactory;
 import org.apache.ace.client.repository.helper.ArtifactHelper;
 import org.apache.ace.client.repository.impl.RepositoryAdminLoginContextImpl.RepositorySetDescriptor;
 import org.apache.ace.client.repository.object.Artifact2FeatureAssociation;
 import org.apache.ace.client.repository.object.ArtifactObject;
 import org.apache.ace.client.repository.object.DeploymentVersionObject;
-import org.apache.ace.client.repository.object.TargetObject;
-import org.apache.ace.client.repository.object.Feature2DistributionAssociation;
-import org.apache.ace.client.repository.object.FeatureObject;
 import org.apache.ace.client.repository.object.Distribution2TargetAssociation;
 import org.apache.ace.client.repository.object.DistributionObject;
+import org.apache.ace.client.repository.object.Feature2DistributionAssociation;
+import org.apache.ace.client.repository.object.FeatureObject;
+import org.apache.ace.client.repository.object.TargetObject;
 import org.apache.ace.client.repository.repository.Artifact2FeatureAssociationRepository;
 import org.apache.ace.client.repository.repository.ArtifactRepository;
 import org.apache.ace.client.repository.repository.DeploymentVersionRepository;
-import org.apache.ace.client.repository.repository.TargetRepository;
-import org.apache.ace.client.repository.repository.Feature2DistributionAssociationRepository;
-import org.apache.ace.client.repository.repository.FeatureRepository;
 import org.apache.ace.client.repository.repository.Distribution2TargetAssociationRepository;
 import org.apache.ace.client.repository.repository.DistributionRepository;
+import org.apache.ace.client.repository.repository.Feature2DistributionAssociationRepository;
+import org.apache.ace.client.repository.repository.FeatureRepository;
+import org.apache.ace.client.repository.repository.TargetRepository;
+import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.repository.Repository;
 import org.apache.ace.repository.ext.BackupRepository;
 import org.apache.ace.repository.ext.CachedRepository;
@@ -78,28 +79,32 @@ import org.osgi.service.useradmin.User;
  * to be used are defined in <code>login(...)</code>.<br>
  */
 public class RepositoryAdminImpl implements RepositoryAdmin {
+    private final static String PREFS_LOCAL_FILE_ROOT = "ClientRepositoryAdmin";
+    private final static String PREFS_LOCAL_FILE_LOCATION = "FileLocation";
+    private final static String PREFS_LOCAL_FILE_CURRENT = "current";
+    private final static String PREFS_LOCAL_FILE_BACKUP = "backup";
+
     /**
      * Maps from interface classes of the ObjectRepositories to their implementations.
      */
     @SuppressWarnings("unchecked")
     private Map<Class<? extends ObjectRepository>, ObjectRepositoryImpl> m_repositories;
 
+    private final String m_sessionID;
+    private final Properties m_sessionProps;
     private final ChangeNotifier m_changeNotifier;
-    private volatile BundleContext m_context; /* Injected by dependency manager */
-    private volatile PreferencesService m_preferences; /* Injected by dependency manager */
-    private volatile LogService m_log; /* Injected by dependency manager */
-
     private final Object m_lock = new Object();
 
-    private final static String PREFS_LOCAL_FILE_ROOT = "ClientRepositoryAdmin";
-    private final static String PREFS_LOCAL_FILE_LOCATION = "FileLocation";
-    private final static String PREFS_LOCAL_FILE_CURRENT = "current";
-    private final static String PREFS_LOCAL_FILE_BACKUP = "backup";
+    // Injected by dependency manager
+    private volatile DependencyManager m_manager;
+    private volatile BundleContext m_context; 
+    private volatile PreferencesService m_preferences;
+    private volatile LogService m_log;
+
     private User m_user;
     private RepositorySet[] m_repositorySets;
 
-    private volatile DependencyManager m_manager;
-    List<Component[]> m_services;
+    private List<Component[]> m_services;
     private ArtifactRepositoryImpl m_artifactRepositoryImpl;
     private FeatureRepositoryImpl m_featureRepositoryImpl;
     private Artifact2FeatureAssociationRepositoryImpl m_artifact2FeatureAssociationRepositoryImpl;
@@ -109,8 +114,7 @@ public class RepositoryAdminImpl impleme
     private Distribution2TargetAssociationRepositoryImpl m_distribution2TargetAssociationRepositoryImpl;
     private DeploymentVersionRepositoryImpl m_deploymentVersionRepositoryImpl;
     private ChangeNotifierManager m_changeNotifierManager;
-    private final String m_sessionID;
-    private final Properties m_sessionProps;
+
 
     public RepositoryAdminImpl(String sessionID) {
         m_sessionID = sessionID;
@@ -135,11 +139,6 @@ public class RepositoryAdminImpl impleme
     }
 
     @SuppressWarnings("unchecked")
-    void initialize(Map<Class<? extends ObjectRepository>, ObjectRepositoryImpl> repositories) {
-        m_repositories = repositories;
-    }
-
-    @SuppressWarnings("unchecked")
     public void start() {
         synchronized (m_lock) {
             initialize(publishRepositories());
@@ -161,6 +160,11 @@ public class RepositoryAdminImpl impleme
     }
 
     @SuppressWarnings("unchecked")
+    void initialize(Map<Class<? extends ObjectRepository>, ObjectRepositoryImpl> repositories) {
+        m_repositories = repositories;
+    }
+
+    @SuppressWarnings("unchecked")
     private Map<Class<? extends ObjectRepository>, ObjectRepositoryImpl> publishRepositories() {
         // create the repository objects, if this is the first time this method is called.
         if (m_artifactRepositoryImpl == null) {
@@ -173,18 +177,23 @@ public class RepositoryAdminImpl impleme
             m_distribution2TargetAssociationRepositoryImpl = new Distribution2TargetAssociationRepositoryImpl(m_distributionRepositoryImpl, m_targetRepositoryImpl, m_changeNotifierManager.getConfiguredNotifier(RepositoryObject.PRIVATE_TOPIC_ROOT, RepositoryObject.PUBLIC_TOPIC_ROOT, Distribution2TargetAssociation.TOPIC_ENTITY_ROOT, m_sessionID));
             m_deploymentVersionRepositoryImpl = new DeploymentVersionRepositoryImpl(m_changeNotifierManager.getConfiguredNotifier(RepositoryObject.PRIVATE_TOPIC_ROOT, RepositoryObject.PUBLIC_TOPIC_ROOT, DeploymentVersionObject.TOPIC_ENTITY_ROOT, m_sessionID));
         }
+        
         // first, register the artifact repository manually; it needs some special care.
         Component artifactRepoService = m_manager.createComponent()
             .setInterface(ArtifactRepository.class.getName(), m_sessionProps)
             .setImplementation(m_artifactRepositoryImpl)
+            .add(m_manager.createServiceDependency().setService(ConnectionFactory.class).setRequired(true))
             .add(m_manager.createServiceDependency().setService(LogService.class).setRequired(false))
             .add(m_manager.createServiceDependency().setService(ArtifactHelper.class).setRequired(false).setAutoConfig(false).setCallbacks(this, "addArtifactHelper", "removeArtifactHelper"));
+        
         Dictionary topic = new Hashtable();
-        topic.put(EventConstants.EVENT_TOPIC, new String[] {});
         topic.put(EventConstants.EVENT_FILTER, "(" + SessionFactory.SERVICE_SID + "=" + m_sessionID + ")");
+        topic.put(EventConstants.EVENT_TOPIC, new String[] {});
+        
         Component artifactHandlerService = m_manager.createComponent()
             .setInterface(EventHandler.class.getName(), topic)
             .setImplementation(m_artifactRepositoryImpl);
+
         m_manager.add(artifactRepoService);
         m_manager.add(artifactHandlerService);
 
@@ -332,7 +341,7 @@ public class RepositoryAdminImpl impleme
 
         synchronized(m_lock) {
             // TODO I don't like this line, it should not be here...
-            ((ArtifactRepositoryImpl) m_repositories.get(ArtifactRepository.class)).setObrBase(impl.getObrBase());
+            ((ArtifactRepository) m_repositories.get(ArtifactRepository.class)).setObrBase(impl.getObrBase());
             login(impl.getUser(), repositorySets);
         }
     }
@@ -409,40 +418,34 @@ public class RepositoryAdminImpl impleme
      */
     @SuppressWarnings("unchecked")
     private RepositorySet[] getRepositorySets(RepositoryAdminLoginContextImpl context) throws IOException {
+        List<RepositorySetDescriptor> descriptors = context.getDescriptors();
+        
         // First, some sanity checks on the list of descriptors.
-        for (RepositorySetDescriptor rsd : context.getDescriptors()) {
+        for (RepositorySetDescriptor rsd : descriptors) {
             for (Class c : rsd.m_objectRepositories) {
                 // Do we have an impl for each repository class?
                 if (!m_repositories.containsKey(c)) {
                     throw new IllegalArgumentException(rsd.toString() + " references repository class " + c.getName() + " for which no implementation is available.");
                 }
-                // Do other sets have a reference to this same class?
-                for (RepositorySetDescriptor other : context.getDescriptors()) {
-                    if (other != rsd) {
-                        for (Class otherC : other.m_objectRepositories) {
-                            if (c.equals(otherC)) {
-                                throw new IllegalArgumentException(rsd.toString() + " references repository class " + c.getName() + ", but so does " + other.toString());
-                            }
-                        }
-                    }
-                }
             }
         }
 
-        RepositorySet[] result = new RepositorySet[context.getDescriptors().size()];
+        RepositorySet[] result = new RepositorySet[descriptors.size()];
 
         /*
          * Create the lists of repositories and topics, and create and register
          * the sets with these.
          */
         for (int i = 0; i < result.length; i++) {
-            RepositorySetDescriptor rsd = context.getDescriptors().get(i);
+            RepositorySetDescriptor rsd = descriptors.get(i);
+            
             ObjectRepositoryImpl[] impls = new ObjectRepositoryImpl[rsd.m_objectRepositories.length];
             String[] topics = new String[rsd.m_objectRepositories.length];
             for (int j = 0; j < impls.length; j++) {
                 impls[j] = m_repositories.get(rsd.m_objectRepositories[j]);
                 topics[j] = impls[j].getTopicAll(true);
             }
+
             result[i] = loadRepositorySet(context.getUser(), rsd, impls);
             result[i].registerHandler(m_context, m_sessionID, topics);
         }
@@ -516,9 +519,9 @@ public class RepositoryAdminImpl impleme
      * Helper method for login.
      * @throws IOException
      */
-    private CachedRepository getCachedRepositoryFromPreferences(User user, Repository repository, Preferences repositoryPrefs) throws IOException {
+    private CachedRepository getCachedRepositoryFromPreferences(Repository repository, Preferences repositoryPrefs) throws IOException {
         long mostRecentVersion = repositoryPrefs.getLong("version", CachedRepositoryImpl.UNCOMMITTED_VERSION);
-        return new CachedRepositoryImpl(user, repository, getBackupFromPreferences(repositoryPrefs), mostRecentVersion);
+        return new CachedRepositoryImpl(repository, getBackupFromPreferences(repositoryPrefs), mostRecentVersion);
     }
 
     /**
@@ -532,9 +535,18 @@ public class RepositoryAdminImpl impleme
     @SuppressWarnings("unchecked")
     public RepositorySet loadRepositorySet(User user, RepositorySetDescriptor rsd, ObjectRepositoryImpl[] repos) throws IOException {
         Repository repo = new RemoteRepository(rsd.m_location, rsd.m_customer, rsd.m_name);
+
+        // Expose the repository itself as component so its dependencies get managed...
+        m_manager.add(m_manager.createComponent()
+            .setImplementation(repo)
+            .add(m_manager.createServiceDependency()
+                .setService(ConnectionFactory.class)
+                .setRequired(true)));
+
         Preferences prefs = m_preferences.getUserPreferences(user.getName());
         Preferences repoPrefs = getRepositoryPrefs(prefs, rsd.m_location, rsd.m_customer, rsd.m_name);
-        return new RepositorySet(m_changeNotifier, m_log, user, repoPrefs, repos, getCachedRepositoryFromPreferences(user, repo, repoPrefs), rsd.m_name, rsd.m_writeAccess);
+
+        return new RepositorySet(m_changeNotifier, m_log, user, repoPrefs, repos, getCachedRepositoryFromPreferences(repo, repoPrefs), rsd.m_name, rsd.m_writeAccess);
     }
 
     public int getNumberWithWorkingState(Class<? extends RepositoryObject> clazz, WorkingState state) {

Modified: ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/impl/RepositoryAdminLoginContextImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/impl/RepositoryAdminLoginContextImpl.java?rev=1329199&r1=1329198&r2=1329199&view=diff
==============================================================================
--- ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/impl/RepositoryAdminLoginContextImpl.java (original)
+++ ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/impl/RepositoryAdminLoginContextImpl.java Mon Apr 23 12:15:01 2012
@@ -20,6 +20,7 @@ package org.apache.ace.client.repository
 
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import org.apache.ace.client.repository.ObjectRepository;
@@ -27,14 +28,15 @@ import org.apache.ace.client.repository.
 import org.apache.ace.client.repository.repository.Artifact2FeatureAssociationRepository;
 import org.apache.ace.client.repository.repository.ArtifactRepository;
 import org.apache.ace.client.repository.repository.DeploymentVersionRepository;
-import org.apache.ace.client.repository.repository.TargetRepository;
-import org.apache.ace.client.repository.repository.Feature2DistributionAssociationRepository;
-import org.apache.ace.client.repository.repository.FeatureRepository;
 import org.apache.ace.client.repository.repository.Distribution2TargetAssociationRepository;
 import org.apache.ace.client.repository.repository.DistributionRepository;
+import org.apache.ace.client.repository.repository.Feature2DistributionAssociationRepository;
+import org.apache.ace.client.repository.repository.FeatureRepository;
+import org.apache.ace.client.repository.repository.TargetRepository;
 import org.osgi.service.useradmin.User;
 
-class RepositoryAdminLoginContextImpl implements RepositoryAdminLoginContext {
+public class RepositoryAdminLoginContextImpl implements RepositoryAdminLoginContext {
+    
     private final String m_sessionid;
     private final User m_user;
     private final List<RepositorySetDescriptor> m_descriptors = new ArrayList<RepositorySetDescriptor>();
@@ -45,43 +47,60 @@ class RepositoryAdminLoginContextImpl im
         m_sessionid = sessionid;
     }
 
-    @SuppressWarnings("unchecked")
-    public RepositoryAdminLoginContext addRepositories(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess, Class<? extends ObjectRepository>... objectRepositories) {
-        if ((repositoryLocation == null) || (repositoryCustomer == null) || (repositoryName == null)) {
-            throw new IllegalArgumentException("No parameter should be null.");
-        }
-        if ((objectRepositories == null) || (objectRepositories.length == 0)) {
-            throw new IllegalArgumentException("objectRepositories should not be null or empty.");
+    /**
+     * {@inheritDoc}
+     */
+    public RepositoryAdminLoginContext add(BaseRepositoryContext<?> repositoryContext) {
+        if (!(repositoryContext instanceof AbstractRepositoryContext)) {
+            throw new IllegalArgumentException("Invalid repository context!");
         }
-        m_descriptors.add(new RepositorySetDescriptor(repositoryLocation, repositoryCustomer, repositoryName, writeAccess, objectRepositories));
+        
+        addDescriptor(((AbstractRepositoryContext<?>) repositoryContext).createDescriptor());
+        
         return this;
     }
 
-    @SuppressWarnings("unchecked")
-    public RepositoryAdminLoginContext addShopRepository(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess) {
-        return addRepositories(repositoryLocation, repositoryCustomer, repositoryName, writeAccess,
-            ArtifactRepository.class,
-            FeatureRepository.class,
-            Artifact2FeatureAssociationRepository.class,
-            DistributionRepository.class,
-            Feature2DistributionAssociationRepository.class);
-    }
-
-    @SuppressWarnings("unchecked")
-    public RepositoryAdminLoginContext addTargetRepository(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess) {
-        return addRepositories(repositoryLocation, repositoryCustomer, repositoryName, writeAccess,
-            TargetRepository.class,
-            Distribution2TargetAssociationRepository.class);
-    }
-
-    @SuppressWarnings("unchecked")
-    public RepositoryAdminLoginContext addDeploymentRepository(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess) {
-        return addRepositories(repositoryLocation, repositoryCustomer, repositoryName, writeAccess,
-            DeploymentVersionRepository.class);
+    /**
+     * @param descriptor the descriptor to add, cannot be <code>null</code>.
+     */
+    public void addDescriptor(RepositorySetDescriptor descriptor) {
+        checkConsistency(descriptor);
+
+        synchronized (m_descriptors) {
+            m_descriptors.add(descriptor);
+        }
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public ShopRepositoryContext createShopRepositoryContext() {
+        return new ShopRepositoryContextImpl();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TargetRepositoryContext createTargetRepositoryContext() {
+        return new TargetRepositoryContextImpl();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public DeploymentRepositoryContext createDeploymentRepositoryContext() {
+        return new DeploymentRepositoryContextImpl();
+    }
+
+    /**
+     * @return a list with all repository set descriptors, never <code>null</code>.
+     */
     public List<RepositorySetDescriptor> getDescriptors() {
-        return m_descriptors;
+        List<RepositorySetDescriptor> result;
+        synchronized (m_descriptors) {
+            result = new ArrayList<RepositorySetDescriptor>(m_descriptors);
+        }
+        return result;
     }
 
     public RepositoryAdminLoginContext setObrBase(URL base) {
@@ -102,10 +121,36 @@ class RepositoryAdminLoginContextImpl im
     }
 
     /**
+     * Checks the consistency of the internal descriptors with the one given.
+     * 
+     * @param descriptor the to-be-added repository set descriptor, cannot be <code>null</code>.
+     */
+    private void checkConsistency(RepositorySetDescriptor descriptor) {
+        List<Class<? extends ObjectRepository>> seenClasses = new ArrayList<Class<? extends ObjectRepository>>();
+        List<String> seenNames = new ArrayList<String>();
+
+        // Presumption: initially we start out without any duplication...
+        for (RepositorySetDescriptor rsd : getDescriptors()) {
+            seenClasses.addAll(Arrays.asList(rsd.m_objectRepositories));
+            seenNames.add(rsd.m_name);
+        }
+        
+        if (seenNames.contains(descriptor.m_name)) {
+            throw new IllegalArgumentException("Duplicate repository name!");
+        }
+        
+        for (Class<? extends ObjectRepository> clazz : descriptor.m_objectRepositories) {
+            if (seenClasses.contains(clazz)) {
+                throw new IllegalArgumentException("Duplicate object repository!");
+            }
+        }
+    }
+
+    /**
      * Helper class to store all relevant information about a repository in a convenient location before
      * we start using it.
      */
-    static class RepositorySetDescriptor {
+    public static final class RepositorySetDescriptor {
         public final URL m_location;
         public final String m_customer;
         public final String m_name;
@@ -114,7 +159,7 @@ class RepositoryAdminLoginContextImpl im
         public final Class<? extends ObjectRepository>[] m_objectRepositories;
 
         @SuppressWarnings("unchecked")
-        RepositorySetDescriptor(URL location, String customer, String name, boolean writeAccess, Class<? extends ObjectRepository>... objectRepositories) {
+        public RepositorySetDescriptor(URL location, String customer, String name, boolean writeAccess, Class<? extends ObjectRepository>... objectRepositories) {
             m_location = location;
             m_customer = customer;
             m_name = name;
@@ -127,4 +172,94 @@ class RepositoryAdminLoginContextImpl im
             return "Repository location " + m_location.toString() + ", customer " + m_customer + ", name " + m_name;
         }
     }
+
+    static abstract class AbstractRepositoryContext<T extends BaseRepositoryContext<?>> implements BaseRepositoryContext<T> 
+    {
+        private URL m_location;
+        private String m_name;
+        private String m_customer;
+        private boolean m_writeable;
+        private final Class<? extends ObjectRepository<?>>[] m_repositories;
+        
+        public AbstractRepositoryContext(Class<? extends ObjectRepository<?>>... repositories) {
+            if (repositories == null || repositories.length == 0) {
+                throw new IllegalArgumentException("Need at least one object repository!");
+            }
+            m_repositories = repositories;
+        }
+        
+        public T setCustomer(String customer) {
+            if (customer == null) {
+                throw new IllegalArgumentException("Customer cannot be null!");
+            }
+            m_customer = customer;
+            return getThis();
+        }
+
+        public T setLocation(URL location) {
+            if (location == null) {
+                throw new IllegalArgumentException("Location cannot be null!");
+            }
+            m_location = location;
+            return getThis();
+        }
+
+        public T setName(String name) {
+            if (name == null) {
+                throw new IllegalArgumentException("Name cannot be null!");
+            }
+            m_name = name;
+            return getThis();
+        }
+
+        public T setWriteable() {
+            m_writeable = true;
+            return getThis();
+        }
+        
+        /**
+         * @return a new repository set descriptor, never <code>null</code>.
+         */
+        final RepositorySetDescriptor createDescriptor() {
+            return new RepositorySetDescriptor(m_location, m_customer, m_name, m_writeable, m_repositories);
+        }
+        
+        abstract T getThis();
+    }
+
+    static final class ShopRepositoryContextImpl extends AbstractRepositoryContext<ShopRepositoryContext> implements ShopRepositoryContext {
+        
+        public ShopRepositoryContextImpl() {
+            super(ArtifactRepository.class, FeatureRepository.class, Artifact2FeatureAssociationRepository.class, DistributionRepository.class, Feature2DistributionAssociationRepository.class);
+        }
+        
+        @Override
+        ShopRepositoryContext getThis() {
+            return this;
+        }
+    }
+
+    static final class TargetRepositoryContextImpl extends AbstractRepositoryContext<TargetRepositoryContext> implements TargetRepositoryContext {
+        
+        public TargetRepositoryContextImpl() {
+            super(TargetRepository.class, Distribution2TargetAssociationRepository.class);
+        }
+        
+        @Override
+        TargetRepositoryContext getThis() {
+            return this;
+        }
+    }
+
+    static final class DeploymentRepositoryContextImpl extends AbstractRepositoryContext<DeploymentRepositoryContext> implements DeploymentRepositoryContext {
+        
+        public DeploymentRepositoryContextImpl() {
+            super(DeploymentVersionRepository.class);
+        }
+        
+        @Override
+        DeploymentRepositoryContext getThis() {
+            return this;
+        }
+    }
 }

Modified: ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/stateful/impl/StatefulTargetRepositoryImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/stateful/impl/StatefulTargetRepositoryImpl.java?rev=1329199&r1=1329198&r2=1329199&view=diff
==============================================================================
--- ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/stateful/impl/StatefulTargetRepositoryImpl.java (original)
+++ ace/trunk/ace-client-repository-impl/src/main/java/org/apache/ace/client/repository/stateful/impl/StatefulTargetRepositoryImpl.java Mon Apr 23 12:15:01 2012
@@ -74,8 +74,7 @@ public class StatefulTargetRepositoryImp
     private LogService m_log; /* Injected by dependency manager */
     private BundleHelper m_bundleHelper; /* Injected by dependency manager */
     // TODO: Make the concurrencyLevel of this concurrent hashmap settable?
-    private Map<String, StatefulTargetObjectImpl> m_repository =
-        new ConcurrentHashMap<String, StatefulTargetObjectImpl>();
+    private Map<String, StatefulTargetObjectImpl> m_repository = new ConcurrentHashMap<String, StatefulTargetObjectImpl>();
     private Map<String, StatefulTargetObjectImpl> m_index = new ConcurrentHashMap<String, StatefulTargetObjectImpl>();
     private final String m_sessionID;
 

Modified: ace/trunk/ace-client-repository-impl/src/test/java/org/apache/ace/client/repository/impl/CachedRepositoryImplTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-repository-impl/src/test/java/org/apache/ace/client/repository/impl/CachedRepositoryImplTest.java?rev=1329199&r1=1329198&r2=1329199&view=diff
==============================================================================
--- ace/trunk/ace-client-repository-impl/src/test/java/org/apache/ace/client/repository/impl/CachedRepositoryImplTest.java (original)
+++ ace/trunk/ace-client-repository-impl/src/test/java/org/apache/ace/client/repository/impl/CachedRepositoryImplTest.java Mon Apr 23 12:15:01 2012
@@ -43,7 +43,7 @@ public class CachedRepositoryImplTest {
         m_repository.commit(new ByteArrayInputStream(testContent), 0);
         BackupRepository m_backupRepository = new MockBackupRepository();
 
-        CachedRepository m_cachedRepository = new CachedRepositoryImpl(null, m_repository, m_backupRepository, 0);
+        CachedRepository m_cachedRepository = new CachedRepositoryImpl(m_repository, m_backupRepository, 0);
 
         InputStream input = m_cachedRepository.checkout(1);
         byte[] inputBytes = AdminTestUtil.copy(input);
@@ -67,13 +67,13 @@ public class CachedRepositoryImplTest {
         Repository m_repository = new MockRepository();
         BackupRepository m_backupRepository = new MockBackupRepository();
 
-        CachedRepository m_cachedRepository = new CachedRepositoryImpl(null, m_repository, m_backupRepository, 0);
+        CachedRepository m_cachedRepository = new CachedRepositoryImpl(m_repository, m_backupRepository, 0);
         byte[] testContent = new byte[] {'i', 'n', 'i', 't', 'i', 'a', 'l'};
 
         InputStream input = new ByteArrayInputStream(testContent);
         m_cachedRepository.commit(input, 0);
 
-        m_cachedRepository = new CachedRepositoryImpl(null, m_repository, m_backupRepository, 0);
+        m_cachedRepository = new CachedRepositoryImpl(m_repository, m_backupRepository, 0);
         input = m_cachedRepository.checkout(1);
         byte[] inputBytes = AdminTestUtil.copy(input);
         assert AdminTestUtil.byteArraysEqual(inputBytes, testContent) : "We got something different than 'initial' from checkout: " + new String(inputBytes);
@@ -84,7 +84,7 @@ public class CachedRepositoryImplTest {
 
         m_cachedRepository.commit();
 
-        m_cachedRepository = new CachedRepositoryImpl(null, m_repository, m_backupRepository, 0);
+        m_cachedRepository = new CachedRepositoryImpl(m_repository, m_backupRepository, 0);
         input = m_cachedRepository.checkout(2);
         inputBytes = AdminTestUtil.copy(input);
         assert AdminTestUtil.byteArraysEqual(inputBytes, newTestContent) : "We got something different than 'new' from checkout: " + new String(inputBytes);
@@ -100,13 +100,13 @@ public class CachedRepositoryImplTest {
         Repository m_repository = new MockRepository();
         BackupRepository m_backupRepository = new MockBackupRepository();
 
-        CachedRepository m_cachedRepository = new CachedRepositoryImpl(null, m_repository, m_backupRepository, 0);
+        CachedRepository m_cachedRepository = new CachedRepositoryImpl(m_repository, m_backupRepository, 0);
         byte[] testContent = new byte[] {'i', 'n', 'i', 't', 'i', 'a', 'l'};
 
         InputStream input = new ByteArrayInputStream(testContent);
         m_cachedRepository.commit(input, 0);
 
-        m_cachedRepository = new CachedRepositoryImpl(null, m_repository, m_backupRepository, 0);
+        m_cachedRepository = new CachedRepositoryImpl(m_repository, m_backupRepository, 0);
         input = m_cachedRepository.checkout(1);
 
         byte[] newTestContent = new byte[] {'n', 'e', 'w'};

Added: ace/trunk/ace-client-repository-impl/src/test/java/org/apache/ace/client/repository/impl/RepositoryAdminLoginContextImplTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-repository-impl/src/test/java/org/apache/ace/client/repository/impl/RepositoryAdminLoginContextImplTest.java?rev=1329199&view=auto
==============================================================================
--- ace/trunk/ace-client-repository-impl/src/test/java/org/apache/ace/client/repository/impl/RepositoryAdminLoginContextImplTest.java (added)
+++ ace/trunk/ace-client-repository-impl/src/test/java/org/apache/ace/client/repository/impl/RepositoryAdminLoginContextImplTest.java Mon Apr 23 12:15:01 2012
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ace.client.repository.impl;
+
+import java.net.URL;
+
+import org.apache.ace.client.repository.impl.RepositoryAdminLoginContextImpl.RepositorySetDescriptor;
+import org.apache.ace.client.repository.repository.ArtifactRepository;
+import org.apache.ace.client.repository.repository.FeatureRepository;
+import org.apache.ace.test.constants.TestConstants;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Test cases for {@link RepositoryAdminLoginContextImpl}.
+ */
+public class RepositoryAdminLoginContextImplTest {
+
+    private static final String CUSTOMER = "apache";
+    
+    private static final String NAME_SHOP = "shop";
+    private static final String NAME_DEPLOYMENT = "deployment";
+    
+    private URL m_location;
+    
+    @Before
+    public void setUp() throws Exception {
+        m_location = new URL("http://localhost:" + TestConstants.PORT);
+    }
+    
+    /**
+     * Test method for {@link RepositoryAdminLoginContextImpl#addDescriptor(RepositorySetDescriptor)}.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddDuplicateObjectRepositoryFail() throws Exception {
+        RepositoryAdminLoginContextImpl context = new RepositoryAdminLoginContextImpl(Mockito.mock(User.class), "12345");
+
+        context.addDescriptor(new RepositorySetDescriptor(m_location, CUSTOMER, NAME_SHOP, true, FeatureRepository.class));
+        context.addDescriptor(new RepositorySetDescriptor(m_location, CUSTOMER, NAME_DEPLOYMENT, true, FeatureRepository.class));
+    }
+
+    /**
+     * Test method for {@link RepositoryAdminLoginContextImpl#addDescriptor(RepositorySetDescriptor)}.
+     */
+    @Test
+    public void testAddDisjointObjectRepositoriesOk() throws Exception {
+        RepositoryAdminLoginContextImpl context = new RepositoryAdminLoginContextImpl(Mockito.mock(User.class), "12345");
+
+        context.addDescriptor(new RepositorySetDescriptor(m_location, CUSTOMER, NAME_SHOP, true, ArtifactRepository.class));
+        context.addDescriptor(new RepositorySetDescriptor(m_location, CUSTOMER, NAME_DEPLOYMENT, true, FeatureRepository.class));
+    }
+
+    /**
+     * Test method for {@link RepositoryAdminLoginContextImpl#addDescriptor(RepositorySetDescriptor)}.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testDuplicateRepositoryNameFail() throws Exception {
+        RepositoryAdminLoginContextImpl context = new RepositoryAdminLoginContextImpl(Mockito.mock(User.class), "12345");
+        
+        context.addDescriptor(new RepositorySetDescriptor(m_location, CUSTOMER, NAME_SHOP, true, ArtifactRepository.class));
+        context.addDescriptor(new RepositorySetDescriptor(m_location, CUSTOMER, NAME_SHOP, true, FeatureRepository.class));
+    }
+}

Propchange: ace/trunk/ace-client-repository-impl/src/test/java/org/apache/ace/client/repository/impl/RepositoryAdminLoginContextImplTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: ace/trunk/ace-client-rest/pom.xml
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-rest/pom.xml?rev=1329199&r1=1329198&r2=1329199&view=diff
==============================================================================
--- ace/trunk/ace-client-rest/pom.xml (original)
+++ ace/trunk/ace-client-rest/pom.xml Mon Apr 23 12:15:01 2012
@@ -60,6 +60,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.ace</groupId>
+            <artifactId>org.apache.ace.connectionfactory</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ace</groupId>
             <artifactId>org.apache.ace.client.repository.api</artifactId>
         </dependency>
          <dependency>

Modified: ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/RESTClientServlet.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/RESTClientServlet.java?rev=1329199&r1=1329198&r2=1329199&view=diff
==============================================================================
--- ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/RESTClientServlet.java (original)
+++ ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/RESTClientServlet.java Mon Apr 23 12:15:01 2012
@@ -57,6 +57,8 @@ public class RESTClientServlet extends H
     private static final String LATEST_FOLDER = "latest";
     /** Name of the folder where working copies are kept. */
     private static final String WORK_FOLDER = "work";
+    /** A boolean denoting whether or not authentication is enabled. */
+    private static final String KEY_USE_AUTHENTICATION = "authentication.enabled";
     /** URL of the repository to talk to. */
     private static final String KEY_REPOSITORY_URL = "repository.url";
     /** URL of the OBR to talk to. */
@@ -69,7 +71,7 @@ public class RESTClientServlet extends H
     private static final String KEY_DISTRIBUTION_REPOSITORY_NAME = "distribution.repository.name";
     /** Name of the deployment repository. */
     private static final String KEY_DEPLOYMENT_REPOSITORY_NAME = "deployment.repository.name";
-    /** Name of the user to log in as. */
+    /** Name of the user to log in as, in case no actual authentication is used. */
     private static final String KEY_USER_NAME = "user.name";
     /** The action name for approving targets. */
     private static final String ACTION_APPROVE = "approve";
@@ -88,6 +90,7 @@ public class RESTClientServlet extends H
     private final Map<String, Component> m_workspaceComponents;
     private final Gson m_gson;
     
+    private boolean m_useAuthentication;
     private String m_repositoryURL;
     private String m_obrURL;
     private String m_customerName;
@@ -110,10 +113,17 @@ public class RESTClientServlet extends H
     }
 
     public void updated(Dictionary properties) throws ConfigurationException {
+        // First check whether all mandatory configuration keys are available...
+        String useAuth = getProperty(properties, KEY_USE_AUTHENTICATION);
+        if (useAuth == null || !("true".equalsIgnoreCase(useAuth) || "false".equalsIgnoreCase(useAuth))) {
+            throw new ConfigurationException(KEY_USE_AUTHENTICATION, "Missing or invalid value!");
+        }
+
         // Note that configuration changes are only applied to new work areas, started after the
         // configuration was changed. No attempt is done to "fix" existing work areas, although we
         // might consider flushing/invalidating them.
         synchronized (m_workspaces) {
+            m_useAuthentication = Boolean.valueOf(useAuth);
             m_repositoryURL = getProperty(properties, KEY_REPOSITORY_URL, "http://localhost:8080/repository");
             m_obrURL = getProperty(properties, KEY_OBR_URL, "http://localhost:8080/obr");
             m_customerName = getProperty(properties, KEY_CUSTOMER_NAME, "apache");
@@ -176,13 +186,25 @@ public class RESTClientServlet extends H
      * @return a property value, can be <code>null</code>.
      */
     String getProperty(Dictionary properties, String key, String defaultValue) {
+        String value = getProperty(properties, key);
+        return (value == null) ? defaultValue : value; 
+    }
+
+    /**
+     * Helper method to safely obtain a property value from the given dictionary.
+     * 
+     * @param properties the dictionary to retrieve the value from, can be <code>null</code>;
+     * @param key the name of the property to retrieve, cannot be <code>null</code>.
+     * @return a property value, can be <code>null</code>.
+     */
+    String getProperty(Dictionary properties, String key) {
         if (properties != null) {
             Object value = properties.get(key);
             if (value != null && value instanceof String) {
                 return (String) value;
             }
         }
-        return defaultValue;
+        return null;
     }
 
     /**
@@ -391,7 +413,7 @@ public class RESTClientServlet extends H
 
         synchronized (m_workspaces) {
             sessionID = "rest-" + m_sessionID++;
-            workspace = new Workspace(sessionID, m_repositoryURL, m_obrURL, m_customerName, m_storeRepositoryName, m_targetRepositoryName, m_deploymentRepositoryName, m_serverUser);
+            workspace = new Workspace(sessionID, m_repositoryURL, m_obrURL, m_customerName, m_storeRepositoryName, m_targetRepositoryName, m_deploymentRepositoryName, m_useAuthentication, m_serverUser);
             m_workspaces.put(sessionID, workspace);
 
             component = m_dm.createComponent().setImplementation(workspace);

Modified: ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/Workspace.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/Workspace.java?rev=1329199&r1=1329198&r2=1329199&view=diff
==============================================================================
--- ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/Workspace.java (original)
+++ ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/Workspace.java Mon Apr 23 12:15:01 2012
@@ -19,6 +19,7 @@
 package org.apache.ace.client.rest;
 
 import java.io.IOException;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -32,6 +33,7 @@ import javax.servlet.http.HttpServletReq
 import org.apache.ace.authentication.api.AuthenticationService;
 import org.apache.ace.client.repository.ObjectRepository;
 import org.apache.ace.client.repository.RepositoryAdmin;
+import org.apache.ace.client.repository.RepositoryAdminLoginContext;
 import org.apache.ace.client.repository.RepositoryObject;
 import org.apache.ace.client.repository.SessionFactory;
 import org.apache.ace.client.repository.repository.Artifact2FeatureAssociationRepository;
@@ -57,14 +59,17 @@ public class Workspace {
     static final String DISTRIBUTION = "distribution";
     static final String DISTRIBUTION2TARGET = "distribution2target";
     static final String TARGET = "target";
+    
     private final String m_sessionID;
-    private final String m_repositoryURL;
-    private final String m_obrURL;
+    private final URL m_repositoryURL;
+    private final URL m_obrURL;
     private final String m_customerName;
     private final String m_storeRepositoryName;
     private final String m_distributionRepositoryName;
     private final String m_deploymentRepositoryName;
     private final String m_serverUser;
+    private final boolean m_useAuthentication;
+    
     private volatile AuthenticationService m_authenticationService;
     private volatile DependencyManager m_manager;
     private volatile RepositoryAdmin m_repositoryAdmin;
@@ -78,14 +83,15 @@ public class Workspace {
     private volatile UserAdmin m_userAdmin;
     private volatile LogService m_log;
 
-    public Workspace(String sessionID, String repositoryURL, String obrURL, String customerName, String storeRepositoryName, String distributionRepositoryName, String deploymentRepositoryName, String serverUser) {
+    public Workspace(String sessionID, String repositoryURL, String obrURL, String customerName, String storeRepositoryName, String distributionRepositoryName, String deploymentRepositoryName, boolean useAuthentication, String serverUser) throws MalformedURLException {
         m_sessionID = sessionID;
-        m_repositoryURL = repositoryURL;
-        m_obrURL = obrURL;
+        m_repositoryURL = new URL(repositoryURL);
+        m_obrURL = new URL(obrURL);
         m_customerName = customerName;
         m_storeRepositoryName = storeRepositoryName;
         m_distributionRepositoryName = distributionRepositoryName;
         m_deploymentRepositoryName = deploymentRepositoryName;
+        m_useAuthentication = useAuthentication;
         m_serverUser = serverUser;
     }
     
@@ -122,7 +128,7 @@ public class Workspace {
         addSessionDependency(component, Artifact2FeatureAssociationRepository.class, true);
         addSessionDependency(component, Feature2DistributionAssociationRepository.class, true);
         addSessionDependency(component, Distribution2TargetAssociationRepository.class, true);
-        addDependency(component, AuthenticationService.class, true);
+        addDependency(component, AuthenticationService.class, m_useAuthentication);
         addDependency(component, UserAdmin.class, true);
         addDependency(component, LogService.class, false);
     }
@@ -135,23 +141,40 @@ public class Workspace {
     
     public boolean login(HttpServletRequest request) {
         try {
-            User user = m_authenticationService.authenticate(request);
-            if (user == null) {
-                // No user obtained through request; try fallback scenario...
-                // TODO this shouldn't be here, but otherwise we break all existing clients
+            final User user;
+            if (m_useAuthentication) {
+                // Use the authentication service to authenticate the given request...
+                user = m_authenticationService.authenticate(request);
+            } else {
+                // Use the "hardcoded" user to login with...
                 user = m_userAdmin.getUser("username", m_serverUser);
-                if (user == null) {
-                    // Still no user obtained; no succesful login...
-                    return false;
-                }
             }
+            
+            if (user == null) {
+                // No user obtained through request/fallback scenario; login failed...
+                return false;
+            }
+
+            RepositoryAdminLoginContext context = m_repositoryAdmin.createLoginContext(user);
+            
+            context.setObrBase(m_obrURL)
+                .add(context.createShopRepositoryContext()
+                    .setLocation(m_repositoryURL)
+                    .setCustomer(m_customerName)
+                    .setName(m_storeRepositoryName)
+                    .setWriteable())
+                .add(context.createTargetRepositoryContext()
+                    .setLocation(m_repositoryURL)
+                    .setCustomer(m_customerName)
+                    .setName(m_distributionRepositoryName)
+                    .setWriteable())
+                .add(context.createDeploymentRepositoryContext()
+                    .setLocation(m_repositoryURL)
+                    .setCustomer(m_customerName)
+                    .setName(m_deploymentRepositoryName)
+                    .setWriteable());
 
-            m_repositoryAdmin.login(m_repositoryAdmin.createLoginContext(user)
-                .setObrBase(new URL(m_obrURL))
-                .addShopRepository(new URL(m_repositoryURL), m_customerName, m_storeRepositoryName, true)
-                .addTargetRepository(new URL(m_repositoryURL), m_customerName, m_distributionRepositoryName, true)
-                .addDeploymentRepository(new URL(m_repositoryURL), m_customerName, m_deploymentRepositoryName, true)
-                );
+            m_repositoryAdmin.login(context);
             m_repositoryAdmin.checkout();
         }
         catch (IOException e) {

Modified: ace/trunk/ace-configurator-useradmin-task/src/main/java/org/apache/ace/configurator/useradmin/task/UpdateUserAdminTask.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-configurator-useradmin-task/src/main/java/org/apache/ace/configurator/useradmin/task/UpdateUserAdminTask.java?rev=1329199&r1=1329198&r2=1329199&view=diff
==============================================================================
--- ace/trunk/ace-configurator-useradmin-task/src/main/java/org/apache/ace/configurator/useradmin/task/UpdateUserAdminTask.java (original)
+++ ace/trunk/ace-configurator-useradmin-task/src/main/java/org/apache/ace/configurator/useradmin/task/UpdateUserAdminTask.java Mon Apr 23 12:15:01 2012
@@ -22,14 +22,17 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.util.Dictionary;
 import java.util.Properties;
 
+import org.apache.ace.repository.Repository;
+import org.apache.ace.repository.ext.BackupRepository;
 import org.apache.ace.repository.ext.CachedRepository;
 import org.apache.ace.repository.ext.impl.CachedRepositoryImpl;
+import org.apache.ace.repository.ext.impl.FilebasedBackupRepository;
 import org.apache.ace.resourceprocessor.useradmin.UserAdminConfigurator;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.cm.ConfigurationException;
 import org.osgi.service.cm.ManagedService;
@@ -56,13 +59,40 @@ public class UpdateUserAdminTask impleme
     private static final String FILE_ROOT = "userrepositories";
     private static final String VERSION = "version";
 
-    private volatile UserAdminConfigurator m_configurator; /* Will be injected by dependency manager */
-    private volatile LogService m_log; /* Will be injected by dependency manager */
-    private volatile BundleContext m_context; /* Will be injected by dependency manager */
+    // Will by injected by Dependency Manager...
+    private volatile UserAdminConfigurator m_configurator;
+    private volatile LogService m_log;
+    private volatile BundleContext m_context;
 
     private CachedRepository m_repo;
+    private BackupRepository m_backup;
+    private String m_repoFilter;
     private File m_properties;
+    
+    /**
+     * Called by Dependency Manager upon initialization of this component.
+     * <p>
+     * Due to the dependency on the configuration; the {@link #updated(Dictionary)} method is already called!
+     * </p>
+     * 
+     * @param comp this component, cannot be <code>null</code>.
+     */
+    public void init(Component comp) {
+        final DependencyManager dm = comp.getDependencyManager();
+        // Add the required dependency to the remote repository...
+        comp.add(dm.createServiceDependency()
+            .setService(Repository.class, m_repoFilter)
+            .setCallbacks("addRepo", "removeRepo")
+            .setInstanceBound(true)
+            .setRequired(true)
+            );
+    }
 
+    /**
+     * Checks whether there are updates to the remote repository, and if so, updates the users' backend with its contents.
+     * 
+     * @see java.lang.Runnable#run()
+     */
     public void run() {
         try {
             if (!m_repo.isCurrent()) {
@@ -78,7 +108,12 @@ public class UpdateUserAdminTask impleme
         }
     }
 
-    public void start() {
+    /**
+     * Called by Dependency Manager upon starting of this component.
+     * 
+     * @param comp this component, cannot be <code>null</code>.
+     */
+    public void start(Component comp) {
         try {
             // Try to read the server data
             m_configurator.setUsers(m_repo.checkout(true));
@@ -96,50 +131,8 @@ public class UpdateUserAdminTask impleme
         }
     }
 
-    /**
-     * Loads the most recent version from the given properties file.
-     */
-    private long loadVersion(File propertiesfile) {
-        long result = CachedRepositoryImpl.UNCOMMITTED_VERSION;
-        try {
-            Properties props = new Properties();
-            props.loadFromXML(new FileInputStream(propertiesfile));
-            result = Long.parseLong((String) props.get(VERSION));
-        }
-        catch (IOException ioe) {
-            // We have no data; no problem.
-        }
-        return result;
-    }
-
-    /**
-     * Saves the most recent version to the given properties file.
-     */
-    private void saveVersion(File properties, Long version) {
-        Properties props = new Properties();
-        props.put(VERSION, version.toString());
-        try {
-            FileOutputStream fileOutputStream = new FileOutputStream(properties);
-            props.storeToXML(fileOutputStream, null);
-        }
-        catch (IOException e) {
-            m_log.log(LogService.LOG_ERROR, "UpdateUserAdminTask failed to save local version number.");
-        }
-    }
-
     public void updated(Dictionary dict) throws ConfigurationException {
         if (dict != null) {
-            String locationString = (String) dict.get(KEY_REPOSITORY_LOCATION);
-            if (locationString == null) {
-                throw new ConfigurationException(KEY_REPOSITORY_LOCATION, "Property missing.");
-            }
-            URL location;
-            try {
-                location = new URL(locationString);
-            }
-            catch (MalformedURLException e) {
-                throw new ConfigurationException(KEY_REPOSITORY_LOCATION, "Location " + locationString + " is not a valid URL.");
-            }
             String customer = (String) dict.get(KEY_REPOSITORY_CUSTOMER);
             if (customer == null) {
                 throw new ConfigurationException(KEY_REPOSITORY_CUSTOMER, "Property missing.");
@@ -149,15 +142,36 @@ public class UpdateUserAdminTask impleme
                 throw new ConfigurationException(KEY_REPOSITORY_NAME, "Property missing.");
             }
 
-            String fileRoot = FILE_ROOT + File.separator + location.getAuthority().replace(':', '-') + location.getPath().replace('/', '\\') + File.separator + customer + File.separator + name + File.separator;
+            String fileRoot = FILE_ROOT + File.separator + customer + File.separator + name + File.separator;
+
             File local = getFile(fileRoot + "local");
             File backup = getFile(fileRoot + "backup");
+            m_backup = new FilebasedBackupRepository(local, backup);
+            
             m_properties = getFile(fileRoot + "properties");
-
-            m_repo = new CachedRepositoryImpl(null, location, customer, name, local, backup, loadVersion(m_properties));
+            
+            m_repoFilter = "(&(customer=" + customer + ")(name=" + name + "))";
         }
     }
 
+    /**
+     * Creates the cached repository when given a remote repository.
+     * 
+     * @param remoteRepo the remote repository to add, cannot be <code>null</code>.
+     */
+    final void addRepo(Repository remoteRepo) {
+        m_repo = new CachedRepositoryImpl(remoteRepo, m_backup, loadVersion(m_properties));
+    }
+
+    /**
+     * Removes the cached repository when given a remote repository.
+     * 
+     * @param remoteRepo the remote repository to remove, cannot be <code>null</code>.
+     */
+    final void removeRepo(Repository remoteRepo) {
+        m_repo = null;
+    }
+
     private File getFile(String name) {
         File result = m_context.getDataFile(name);
         if (!result.exists()) {
@@ -174,4 +188,34 @@ public class UpdateUserAdminTask impleme
         return result;
     }
 
+    /**
+     * Loads the most recent version from the given properties file.
+     */
+    private long loadVersion(File propertiesfile) {
+        long result = CachedRepositoryImpl.UNCOMMITTED_VERSION;
+        try {
+            Properties props = new Properties();
+            props.loadFromXML(new FileInputStream(propertiesfile));
+            result = Long.parseLong((String) props.get(VERSION));
+        }
+        catch (IOException ioe) {
+            // We have no data; no problem.
+        }
+        return result;
+    }
+
+    /**
+     * Saves the most recent version to the given properties file.
+     */
+    private void saveVersion(File properties, Long version) {
+        Properties props = new Properties();
+        props.put(VERSION, version.toString());
+        try {
+            FileOutputStream fileOutputStream = new FileOutputStream(properties);
+            props.storeToXML(fileOutputStream, null);
+        }
+        catch (IOException e) {
+            m_log.log(LogService.LOG_ERROR, "UpdateUserAdminTask failed to save local version number.");
+        }
+    }
 }
\ No newline at end of file

Propchange: ace/trunk/ace-connectionfactory/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Apr 23 12:15:01 2012
@@ -0,0 +1,14 @@
+.metadata
+.settings
+bin
+.classpath
+.project
+target
+*.ipr
+*.iws
+*.iml
+store
+test-output
+velocity.log
+
+

Added: ace/trunk/ace-connectionfactory/pom.xml
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/pom.xml?rev=1329199&view=auto
==============================================================================
--- ace/trunk/ace-connectionfactory/pom.xml (added)
+++ ace/trunk/ace-connectionfactory/pom.xml Mon Apr 23 12:15:01 2012
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.ace</groupId>
+        <artifactId>ace-pom</artifactId>
+        <version>0.8.1-SNAPSHOT</version>
+        <relativePath>../pom/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.ace.connectionfactory</artifactId>
+
+    <name>Apache ACE :: Connection Factory</name>
+    <description>Provides a service for creating connections to remote resources.</description>
+    <packaging>bundle</packaging>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/ace/trunk/ace-connectionfactory</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/ace/trunk/ace-connectionfactory</developerConnection>
+        <url>http://svn.apache.org/repos/asf/ace/trunk/ace-connectionfactory</url>
+    </scm>
+        
+    <properties>
+        <import.package>
+        	org.apache.ace.connectionfactory;version=${project.version},
+        	org.osgi.framework,
+        	org.osgi.service.cm,
+        	org.osgi.service.useradmin
+        </import.package>
+        <export.package>
+        	org.apache.ace.connectionfactory;version=${project.version},
+        </export.package>
+        <private.package>
+            org.apache.ace.connectionfactory.impl,
+            org.apache.commons.codec,
+			org.apache.commons.codec.binary
+        </private.package>
+        <bundle.activator>org.apache.ace.connectionfactory.impl.Activator</bundle.activator>
+    </properties>
+    
+    <dependencies>
+		<dependency>
+			<groupId>org.osgi</groupId>
+		    <artifactId>org.osgi.core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+		    <artifactId>org.osgi.compendium</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>commons-codec</groupId>
+			<artifactId>commons-codec</artifactId>
+		</dependency>
+    </dependencies>
+</project>

Propchange: ace/trunk/ace-connectionfactory/pom.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Added: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/ConnectionFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/ConnectionFactory.java?rev=1329199&view=auto
==============================================================================
--- ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/ConnectionFactory.java (added)
+++ ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/ConnectionFactory.java Mon Apr 23 12:15:01 2012
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.connectionfactory;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.osgi.service.useradmin.User;
+
+/**
+ * Provides a service to create {@link URLConnection}s. The connection factory will be responsible 
+ * for supplying the necessary credentials to ensure the authentication of the connection succeeds.
+ */
+public interface ConnectionFactory {
+
+    /**
+     * Creates a new connection using the given URL, using the (optional) credentials.
+     * 
+     * @param url the URL to connect to, cannot be <code>null</code>.
+     * @return a {@link URLConnection} instance, never <code>null</code>.
+     * @throws IllegalArgumentException in case the given URL was <code>null</code>;
+     * @throws IOException in case the creation of the connection failed.
+     */
+    URLConnection createConnection(URL url) throws IOException;
+
+    /**
+     * Creates a new connection using the given URL, using the (optional) credentials.
+     * 
+     * @param url the URL to connect to, cannot be <code>null</code>;
+     * @param user the user to fetch the credentials from, cannot be <code>null</code>.
+     * @return a {@link URLConnection} instance, never <code>null</code>.
+     * @throws IllegalArgumentException in case the given URL was <code>null</code>, or when the supplied credentials are missing information;
+     * @throws IOException in case the creation of the connection failed.
+     */
+    URLConnection createConnection(URL url, User user) throws IOException;
+}

Propchange: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/ConnectionFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/Activator.java?rev=1329199&view=auto
==============================================================================
--- ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/Activator.java (added)
+++ ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/Activator.java Mon Apr 23 12:15:01 2012
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ace.connectionfactory.impl;
+
+import java.util.Properties;
+
+import org.apache.ace.connectionfactory.ConnectionFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedServiceFactory;
+
+/**
+ * Provides the bundle activator for the {@link ConnectionFactoryImpl} service.
+ */
+public class Activator implements BundleActivator {
+
+    /**
+     * {@inheritDoc}
+     */
+    public void start(BundleContext context) throws Exception {
+        Properties props = new Properties();
+        props.put(Constants.SERVICE_PID, ConnectionFactoryImpl.FACTORY_PID);
+        props.put("impl.type", "jdk");
+
+        context.registerService(new String[]{ ConnectionFactory.class.getName(), ManagedServiceFactory.class.getName() }, 
+            new ConnectionFactoryImpl(), props);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void stop(BundleContext context) throws Exception {
+        // Nop
+    }
+}

Propchange: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/Activator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImpl.java?rev=1329199&view=auto
==============================================================================
--- ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImpl.java (added)
+++ ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImpl.java Mon Apr 23 12:15:01 2012
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.connectionfactory.impl;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ace.connectionfactory.ConnectionFactory;
+import org.apache.ace.connectionfactory.impl.UrlCredentials.AuthType;
+import org.apache.ace.connectionfactory.impl.UrlCredentialsFactory.MissingValueException;
+import org.apache.commons.codec.binary.Base64;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Provides a default implementation for {@link ConnectionFactory} based on the standard <code>java.net</code> 
+ * implementation of {@link URLConnection}.
+ */
+public class ConnectionFactoryImpl implements ConnectionFactory, ManagedServiceFactory {
+
+    public static final String FACTORY_PID = "org.apache.ace.connectionfactory";
+
+    private static final String HTTP_HEADER_AUTHORIZATION = "Authorization";
+    
+    private final Map<String /* config PID */, UrlCredentials> m_credentialMapping;
+    
+    /**
+     * Creates a new {@link ConnectionFactoryImpl}.
+     */
+    public ConnectionFactoryImpl() {
+        m_credentialMapping = new HashMap<String, UrlCredentials>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public URLConnection createConnection(URL url) throws IOException {
+        if (url == null) {
+            throw new IllegalArgumentException("URL cannot be null!");
+        }
+
+        URLConnection conn = url.openConnection();
+
+        UrlCredentials creds = getCredentials(url);
+        if (creds != null) {
+            supplyCredentials(conn, creds);
+        }
+
+        return conn;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public URLConnection createConnection(URL url, User user) throws IOException {
+        if (url == null) {
+            throw new IllegalArgumentException("URL cannot be null!");
+        }
+        if (user == null) {
+            throw new IllegalArgumentException("User cannot be null!");
+        }
+
+        URLConnection conn = url.openConnection();
+
+        UrlCredentials creds = getCredentials(url);
+        if (creds != null) {
+            // TODO apply user!
+            supplyCredentials(conn, creds);
+        }
+
+        return conn;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void deleted(String pid) {
+        synchronized (m_credentialMapping) {
+            m_credentialMapping.remove(pid);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getName() {
+        return "HTTP Connection Factory";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void updated(String pid, Dictionary properties) throws ConfigurationException {
+        UrlCredentials creds;
+        synchronized (m_credentialMapping) {
+            creds = m_credentialMapping.get(pid);
+        }
+
+        if (creds == null) {
+            try {
+                creds = UrlCredentialsFactory.getCredentials(properties);
+                
+                synchronized (m_credentialMapping) {
+                    m_credentialMapping.put(pid, creds);
+                }
+            }
+            catch (MissingValueException e) {
+                throw new ConfigurationException(e.getProperty(), e.getMessage());
+            }
+        }
+    }
+    
+    /**
+     * Returns the credentials to access the given URL.
+     * 
+     * @param url the URL to find the credentials for, cannot be <code>null</code>.
+     * @return a {@link UrlCredentials} instance for the given URL, or <code>null</code> 
+     *         if none were found, or if none were necessary.
+     */
+    private UrlCredentials getCredentials(URL url) {
+        Collection<UrlCredentials> creds;
+        synchronized (m_credentialMapping) {
+            creds = new ArrayList<UrlCredentials>(m_credentialMapping.values());
+        }
+        
+        for (UrlCredentials c : creds) {
+            if (c.matches(url)) {
+                return c;
+            }
+        }
+        
+        return null;
+    }
+
+    /**
+     * Returns the authorization header for HTTP Basic Authentication.
+     * 
+     * @param creds the credentials to supply.
+     * @return a string that denotes the basic authentication header ("Basic " + encoded credentials), never <code>null</code>.
+     */
+    private String getBasicAuthCredentials(UrlCredentials creds) {
+        final Object[] values = creds.getCredentials();
+        if (values.length < 2) {
+            throw new IllegalArgumentException("Insufficient credentials passed! Expected 2 values, got " + values.length + " values.");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        if (values[0] instanceof String) {
+            sb.append((String) values[0]);
+        }
+        else if (values[0] instanceof byte[]) {
+            sb.append(new String((byte[]) values[0]));
+        }
+        sb.append(':');
+        if (values[1] instanceof String) {
+            sb.append((String) values[1]);
+        }
+        else if (values[1] instanceof byte[]) {
+            sb.append(new String((byte[]) values[1]));
+        }
+
+        return "Basic " + Base64.encodeBase64String(sb.toString().getBytes());
+    }
+
+    /**
+     * Supplies the actual credentials to the given {@link URLConnection}.
+     * 
+     * @param conn the connection to supply the credentials to, cannot be <code>null</code>;
+     * @param creds the credentials to supply, cannot be <code>null</code>.
+     * @throws IOException in case of I/O problems.
+     */
+    private void supplyCredentials(URLConnection conn, UrlCredentials creds) throws IOException {
+        final AuthType type = creds.getType();
+        
+        if (AuthType.BASIC.equals(type)) {
+            if (conn instanceof HttpURLConnection) {
+                conn.setRequestProperty(HTTP_HEADER_AUTHORIZATION, getBasicAuthCredentials(creds));
+            }
+        }
+        else if (!AuthType.NONE.equals(type)) {
+            throw new IllegalArgumentException("Unknown authentication type: " + type);
+        }
+    }
+}

Propchange: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/ConnectionFactoryImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentials.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentials.java?rev=1329199&view=auto
==============================================================================
--- ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentials.java (added)
+++ ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentials.java Mon Apr 23 12:15:01 2012
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ace.connectionfactory.impl;
+
+import java.net.URL;
+import java.util.Arrays;
+
+/**
+ * Small container for holding URL credentials.
+ */
+final class UrlCredentials {
+
+    static enum AuthType {
+        /** Indicates no authentication. */
+        NONE,
+        /** Indicates basic HTTP authentication. */
+        BASIC;
+    }
+
+    private final AuthType m_type;
+    private final URL m_baseURL;
+    private final Object[] m_credentials;
+
+    /**
+     * Creates a new, anonymous, {@link UrlCredentials} instance.
+     * 
+     * @param baseURL the base URL for which to apply the credentials, cannot be <code>null</code>.
+     */
+    public UrlCredentials(URL baseURL) {
+        this(AuthType.NONE, baseURL);
+    }
+
+    /**
+     * Creates a new {@link UrlCredentials} instance.
+     * 
+     * @param type the authentication type to use for the authentication of the URL, cannot be <code>null</code>;
+     * @param baseURL the base URL for which to apply the credentials, cannot be <code>null</code>;
+     * @param credentials the credentials to use, cannot be <code>null</code>, but may be empty.
+     */
+    public UrlCredentials(AuthType type, URL baseURL, Object... credentials) {
+        m_type = type;
+        m_baseURL = baseURL;
+        m_credentials = (credentials == null) ? new Object[0] : credentials.clone();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+
+        UrlCredentials other = (UrlCredentials) obj;
+        if (m_type != other.m_type) {
+            return false;
+        }
+        if (!m_baseURL.equals(other.m_baseURL)) {
+            return false;
+        }
+        if (!Arrays.equals(m_credentials, other.m_credentials)) {
+            return false;
+        }
+        return true;
+    }
+    
+    /**
+     * Returns whether or not the given URL can be mapped to our own base URL.
+     * 
+     * @param url the URL to map, may be <code>null</code> in which case <code>false</code> will be returned.
+     * @return <code>true</code> if the given URL maps to our base URL, <code>false</code> otherwise.
+     */
+    public boolean matches(URL url) {
+        if (url == null) {
+            return false;
+        }
+        
+        String baseURL = m_baseURL.toExternalForm();
+        return url.toExternalForm().startsWith(baseURL);
+    }
+
+    /**
+     * Returns the credentials for a URL.
+     * 
+     * @return the credentials, never <code>null</code>.
+     */
+    public Object[] getCredentials() {
+        return m_credentials.clone();
+    }
+
+    /**
+     * Returns the authentication type.
+     * 
+     * @return the type of authentication to use.
+     */
+    public AuthType getType() {
+        return m_type;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((m_type == null) ? 0 : m_type.hashCode());
+        result = prime * result + ((m_baseURL == null) ? 0 : m_baseURL.hashCode());
+        result = prime * result + Arrays.hashCode(m_credentials);
+        return result;
+    }
+}

Propchange: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentials.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactory.java?rev=1329199&view=auto
==============================================================================
--- ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactory.java (added)
+++ ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactory.java Mon Apr 23 12:15:01 2012
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ace.connectionfactory.impl;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Dictionary;
+
+import org.apache.ace.connectionfactory.impl.UrlCredentials.AuthType;
+
+/**
+ * Provides a helper class for obtaining access credentials from a dictionary.
+ */
+final class UrlCredentialsFactory {
+
+    public static final class MissingValueException extends RuntimeException {
+        private final String m_property;
+
+        /**
+         * @param property the name of the missing property;
+         * @param message the message explaining the missing property. 
+         */
+        public MissingValueException(String property) {
+            this(property, "No value for " + property + " given!");
+        }
+
+        /**
+         * @param property the name of the missing property;
+         * @param message the message explaining the missing property. 
+         */
+        public MissingValueException(String property, String message) {
+            super(message);
+            m_property = property;
+        }
+        
+        /**
+         * Returns the name of the missing property.
+         * @return the property name, never <code>null</code>.
+         */
+        public String getProperty() {
+            return m_property;
+        }
+    }
+
+    public static final String KEY_AUTH_BASE_URL = "authentication.baseURL";
+    public static final String KEY_AUTH_TYPE = "authentication.type";
+    public static final String KEY_AUTH_USER_NAME = "authentication.user.name";
+    public static final String KEY_AUTH_USER_PASSWORD = "authentication.user.password";
+    
+    /**
+     * Not used.
+     */
+    private UrlCredentialsFactory() {
+        // Nop
+    }
+
+    /**
+     * @param props the properties to take the access credentials from.
+     * @throws MissingValueException in case the given properties is missing values.
+     */
+    public static UrlCredentials getCredentials(Dictionary props) throws MissingValueException {
+        return getCredentials(props, "");
+    }
+    
+    /**
+     * @param props the properties to take the access credentials from;
+     * @param prefix the prefix to use to lookup the correct values in the given dictionary.
+     * @throws MissingValueException in case the given properties is missing values.
+     */
+    public static UrlCredentials getCredentials(Dictionary props, String prefix) throws MissingValueException {
+        AuthType type;
+        URL baseURL; 
+        Object[] creds;
+        
+        String baseUrlValue = getStringProperty(props, prefix.concat(KEY_AUTH_BASE_URL));
+        if (baseUrlValue == null) {
+            throw new MissingValueException(KEY_AUTH_BASE_URL);
+        }
+        
+        try {
+            baseURL = new URL(baseUrlValue);
+        }
+        catch (MalformedURLException e) {
+            throw new MissingValueException(KEY_AUTH_BASE_URL, "Invalid base URL!");
+        }
+
+        String authType = getStringProperty(props, prefix.concat(KEY_AUTH_TYPE), "none");
+        try {
+            type = AuthType.valueOf(authType.toUpperCase());
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("Unsupported authentication type: " + authType);
+        }
+        
+        if (AuthType.NONE.equals(type)) {
+            creds = new Object[0];
+        } else if (AuthType.BASIC.equals(type)) {
+            String userName = getStringProperty(props, prefix.concat(KEY_AUTH_USER_NAME));
+            if (userName == null) {
+                throw new MissingValueException(prefix.concat(KEY_AUTH_USER_NAME));
+            }
+
+            String password = getStringProperty(props, prefix.concat(KEY_AUTH_USER_PASSWORD));
+            if (password == null) {
+                throw new MissingValueException(prefix.concat(KEY_AUTH_USER_PASSWORD));
+            }
+
+            creds = new Object[] { userName, password };
+        } else {
+            throw new IllegalArgumentException("Invalid/unhandled authentication type: " + authType);
+        }
+
+        return new UrlCredentials(type, baseURL, creds);
+    }
+
+    private static String getStringProperty(Dictionary dict, String key) {
+        Object value = dict.get(key);
+        if (value instanceof String) {
+            return (String) value;
+        } else if (value instanceof byte[]) {
+            return new String((byte[]) value);
+        }
+        return null;
+    }
+    
+    private static String getStringProperty(Dictionary dict, String key, String defaultValue) {
+        String value = getStringProperty(dict, key);
+        return (value == null) ? defaultValue : value;
+    }
+}

Propchange: ace/trunk/ace-connectionfactory/src/main/java/org/apache/ace/connectionfactory/impl/UrlCredentialsFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native