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 2016/01/28 14:29:17 UTC

svn commit: r1727343 - in /ace/trunk: org.apache.ace.repository.itest/src/org/apache/ace/it/repository/ org.apache.ace.repository/src/org/apache/ace/repository/impl/ org.apache.ace.test/src/org/apache/ace/it/

Author: jawi
Date: Thu Jan 28 13:29:17 2016
New Revision: 1727343

URL: http://svn.apache.org/viewvc?rev=1727343&view=rev
Log:
ACE-425 - remove the use of the PreferenceService:

- use a simple in-memory index to check whether the customer + name is unique
  across all instantiated repositories;
- added a test case to verify we cannot instantiate a repository with the same
  customer and name twice.


Modified:
    ace/trunk/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/RepositoryTest.java
    ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java
    ace/trunk/org.apache.ace.test/src/org/apache/ace/it/IntegrationTestBase.java
    ace/trunk/org.apache.ace.test/src/org/apache/ace/it/packageinfo

Modified: ace/trunk/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/RepositoryTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/RepositoryTest.java?rev=1727343&r1=1727342&r2=1727343&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/RepositoryTest.java (original)
+++ ace/trunk/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/RepositoryTest.java Thu Jan 28 13:29:17 2016
@@ -46,6 +46,8 @@ import org.osgi.util.tracker.ServiceTrac
  * Integration test for our repositories, and the replication thereof.
  */
 public class RepositoryTest extends IntegrationTestBase {
+    private static final String REPOSITORY_FACTORY_PID = "org.apache.ace.server.repository.factory";
+
     private static final String REPOSITORY_NAME = "name";
     private static final String REPOSITORY_CUSTOMER = "customer";
     private static final String REPOSITORY_MASTER = "master";
@@ -169,6 +171,49 @@ public class RepositoryTest extends Inte
         removeRepository("testInstance");
     }
 
+    /**
+     * ACE-425
+     */
+    public void testCannotAddDuplicateRepository() throws Exception {
+        String customer = "apache";
+        String name = "testRepo";
+
+        int initialSize = countServices(Repository.class);
+
+        // Publish configuration for a repository instance
+        Dictionary<String, Object> props = new Hashtable<>();
+        props.put(REPOSITORY_CUSTOMER, customer);
+        props.put(REPOSITORY_NAME, name);
+        props.put(REPOSITORY_BASE_DIR, "");
+        props.put(REPOSITORY_FILE_EXTENSION, "");
+        props.put(REPOSITORY_MASTER, "true");
+        props.put("factory.instance.pid", "instance1");
+
+        Configuration config1 = createFactoryConfiguration(REPOSITORY_FACTORY_PID);
+        config1.update(props); // should succeed.
+
+        Thread.sleep(500);
+
+        assertEquals(initialSize + 1, countServices(Repository.class));
+
+        props.put(REPOSITORY_FILE_EXTENSION, ".gz");
+        config1.update(props); // still should succeed.
+
+        Thread.sleep(500);
+
+        // Not updated...
+        assertEquals(initialSize + 1, countServices(Repository.class));
+
+        Configuration config2 = createFactoryConfiguration(REPOSITORY_FACTORY_PID);
+        props.put("factory.instance.pid", "instance2");
+        config2.update(props); // should fail.
+
+        Thread.sleep(500);
+
+        // Not updated...
+        assertEquals(initialSize + 1, countServices(Repository.class));
+    }
+
     public void testInitialContent() throws Exception {
         addRepository("testInstance", "apache", "test", null, null, "somecontent", true);
 
@@ -235,7 +280,7 @@ public class RepositoryTest extends Inte
             props.put(REPOSITORY_INITIAL_CONTENT, initial);
         }
         props.put("factory.instance.pid", instanceName);
-        Configuration config = createFactoryConfiguration("org.apache.ace.server.repository.factory");
+        Configuration config = createFactoryConfiguration(REPOSITORY_FACTORY_PID);
         config.update(props);
 
         try {
@@ -280,14 +325,7 @@ public class RepositoryTest extends Inte
     }
 
     private void removeRepository(String instanceName) throws IOException, InterruptedException, InvalidSyntaxException {
-        Configuration[] configs = null;
-        try {
-            configs = listConfigurations("(factory.instance.pid=" + instanceName + ")");
-        }
-        catch (InvalidSyntaxException e) {
-            // should not happen
-        }
-
+        Configuration[] configs = listConfigurations("(factory.instance.pid=" + instanceName + ")");
         if ((configs != null) && (configs.length > 0)) {
             final Semaphore sem = new Semaphore(0);
             ServiceTracker<Object, Object> tracker = new ServiceTracker<Object, Object>(m_bundleContext, m_bundleContext.createFilter("(factory.instance.pid=" + instanceName + ")"), null) {

Modified: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java?rev=1727343&r1=1727342&r2=1727343&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java (original)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java Thu Jan 28 13:29:17 2016
@@ -30,6 +30,8 @@ import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
@@ -41,9 +43,6 @@ import org.osgi.framework.BundleContext;
 import org.osgi.service.cm.ConfigurationException;
 import org.osgi.service.cm.ManagedServiceFactory;
 import org.osgi.service.log.LogService;
-import org.osgi.service.prefs.BackingStoreException;
-import org.osgi.service.prefs.Preferences;
-import org.osgi.service.prefs.PreferencesService;
 
 /**
  * A <code>ManagedServiceFactory</code> responsible for creating a (<code>Replication</code>)<code>Repository</code>
@@ -51,14 +50,56 @@ import org.osgi.service.prefs.Preference
  */
 public class RepositoryFactory implements ManagedServiceFactory {
 
+    public static class Entry {
+        private final String m_customer;
+        private final String m_name;
+
+        public Entry(String customer, String name) {
+            m_customer = customer;
+            m_name = name;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || getClass() != obj.getClass()) {
+                return false;
+            }
+            Entry other = (Entry) obj;
+            if (!m_customer.equals(other.m_customer)) {
+                return false;
+            }
+            if (!m_name.equals(other.m_name)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 37;
+            int result = 1;
+            result = prime * result + ((m_customer == null) ? 0 : m_customer.hashCode());
+            result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
+            return result;
+        }
+        
+        @Override
+        public String toString() {
+            return String.format("%s :: %s", m_customer, m_name);
+        }
+    }
+
     private final ConcurrentMap<String, Component> m_instances = new ConcurrentHashMap<String, Component>();
+    private final ConcurrentMap<Entry, String> m_index = new ConcurrentHashMap<>();
     private final DependencyManager m_manager;
 
     /* injected by dependency manager */
     private volatile LogService m_log;
     private volatile BundleContext m_context;
-    private volatile PreferencesService m_prefsService;
-
+    // locally managed
     private File m_tempDir;
 
     public RepositoryFactory(DependencyManager manager) {
@@ -69,13 +110,21 @@ public class RepositoryFactory implement
         // remove repository service...
         Component service = m_instances.remove(pid);
         if (service != null) {
-            File repoDir = ((RepositoryImpl) service.getInstance()).getDir();
+            RepositoryImpl repository = (RepositoryImpl) service.getInstance();
+            File repoDir = repository.getDir();
 
             m_manager.remove(service);
+            
+            // update our local index...
+            Map<Entry, String> index = new HashMap<>(m_index);
+            for (Map.Entry<Entry, String> entry : index.entrySet()) {
+                if (pid.equals(entry.getValue())) {
+                    m_index.remove(entry.getKey(), entry.getValue());
+                }
+            }
 
             // remove persisted data...
             deleteRepositoryStore(pid, repoDir);
-            deleteRepositoryPrefs(pid);
         }
     }
 
@@ -84,7 +133,10 @@ public class RepositoryFactory implement
         return "RepositoryFactory";
     }
 
-    public void init() {
+    /**
+     * Called by Felix DM.
+     */
+    public void init() throws IOException {
         m_tempDir = ensureDirectoryAvailable(m_context.getDataFile("tmp"));
     }
 
@@ -113,6 +165,13 @@ public class RepositoryFactory implement
         if ((name == null) || "".equals(name)) {
             throw new ConfigurationException(REPOSITORY_NAME, "Repository name has to be specified.");
         }
+        
+        // Check whether the combination of customer and name is unique...
+        Entry newEntry = new Entry(customer, name);
+        String oldPid = m_index.putIfAbsent(newEntry, pid);
+        if (oldPid != null && !pid.equals(oldPid)) {
+            throw new ConfigurationException(null, "Name and customer combination already exists");
+        }
 
         String master = (String) dict.get(REPOSITORY_MASTER);
         if (!("false".equalsIgnoreCase(master.trim()) || "true".equalsIgnoreCase(master.trim()))) {
@@ -158,8 +217,6 @@ public class RepositoryFactory implement
         Component oldService = m_instances.putIfAbsent(pid, service);
         if (oldService == null) {
             // new instance...
-            createRepositoryPrefs(pid, customer, name);
-
             m_manager.add(service);
         }
         else {
@@ -178,36 +235,6 @@ public class RepositoryFactory implement
         }
     }
 
-    private void createRepositoryPrefs(String pid, String customer, String name) throws ConfigurationException {
-        Preferences systemPrefs = m_prefsService.getSystemPreferences();
-
-        String[] nodes;
-        try {
-            nodes = systemPrefs.childrenNames();
-        }
-        catch (BackingStoreException e) {
-            throw new ConfigurationException(null, "Internal error while validating configuration.");
-        }
-        for (String nodeName : nodes) {
-            if (pid.equals(nodeName)) {
-                // avoid failing on our own node (in case we're updating)...
-                continue;
-            }
-
-            Preferences prefs = systemPrefs.node(nodeName);
-            String repoName = prefs.get(REPOSITORY_NAME, "");
-            String repoCustomer = prefs.get(REPOSITORY_CUSTOMER, "");
-
-            if (name.equalsIgnoreCase(repoName) && name.equalsIgnoreCase(repoCustomer)) {
-                throw new ConfigurationException(null, "Name and customer combination already exists");
-            }
-        }
-
-        Preferences node = systemPrefs.node(pid);
-        node.put(REPOSITORY_NAME, name);
-        node.put(REPOSITORY_CUSTOMER, customer);
-    }
-
     private RepositoryImpl createRepositoryStore(String pid, File baseDir, boolean isMaster, long limitValue, String fileExtension, String initialContents) {
         File dir = ensureDirectoryAvailable(new File(baseDir, pid));
         RepositoryImpl store = new RepositoryImpl(dir, m_tempDir, fileExtension, isMaster, limitValue);
@@ -225,18 +252,6 @@ public class RepositoryFactory implement
         return store;
     }
 
-    private void deleteRepositoryPrefs(String pid) {
-        try {
-            Preferences prefs = m_prefsService.getSystemPreferences();
-
-            prefs.node(pid).removeNode();
-            prefs.sync();
-        }
-        catch (BackingStoreException e) {
-            // Not much we can do
-        }
-    }
-
     private void deleteRepositoryStore(String pid, File repoDir) {
         if (repoDir.exists() && repoDir.isDirectory()) {
             File[] files = repoDir.listFiles();

Modified: ace/trunk/org.apache.ace.test/src/org/apache/ace/it/IntegrationTestBase.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.test/src/org/apache/ace/it/IntegrationTestBase.java?rev=1727343&r1=1727342&r2=1727343&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.test/src/org/apache/ace/it/IntegrationTestBase.java (original)
+++ ace/trunk/org.apache.ace.test/src/org/apache/ace/it/IntegrationTestBase.java Thu Jan 28 13:29:17 2016
@@ -556,6 +556,33 @@ public class IntegrationTestBase extends
     }
 
     /**
+     * @param filter
+     * @return the number of configurations that match the given filter, &gt;= 0.
+     */
+    protected int countConfigurations(String filter) throws IOException, InvalidSyntaxException {
+        ConfigurationAdmin admin = getService(ConfigurationAdmin.class);
+        Configuration[] configs = admin.listConfigurations(filter);
+        return configs == null ? 0 : configs.length;
+    }
+
+    /**
+     * @param type
+     * @return the number of services are registered with the given type, &gt;= 0.
+     */
+    protected int countServices(Class<?> type) throws IOException, InvalidSyntaxException {
+        return countServices(String.format("(%s=%s)", Constants.OBJECTCLASS, type.getName()));
+    }
+    
+    /**
+     * @param filter
+     * @return the number of services that match the given filter, &gt;= 0.
+     */
+    protected int countServices(String filter) throws IOException, InvalidSyntaxException {
+        ServiceReference<?>[] serviceRefs = m_bundleContext.getServiceReferences((String) null, filter);
+        return serviceRefs == null ? 0 : serviceRefs.length;
+    }
+
+    /**
      * Sets whether or not any of the tracked configurations should be automatically be deleted when ending a test.
      * 
      * @param aClean

Modified: ace/trunk/org.apache.ace.test/src/org/apache/ace/it/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.test/src/org/apache/ace/it/packageinfo?rev=1727343&r1=1727342&r2=1727343&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.test/src/org/apache/ace/it/packageinfo (original)
+++ ace/trunk/org.apache.ace.test/src/org/apache/ace/it/packageinfo Thu Jan 28 13:29:17 2016
@@ -1 +1 @@
-version 1.1.0
\ No newline at end of file
+version 1.2.0
\ No newline at end of file