ソースを参照

konfiguracja poprawnie w oparciu o ServiceLoader

Ranides Atterwim 13 年 前
コミット
03a21ab077

+ 1 - 1
pom.xml

@@ -4,7 +4,7 @@
 
     <groupId>net.ranides</groupId>
     <artifactId>assira</artifactId>
-    <version>0.53</version>
+    <version>0.54</version>
     <packaging>jar</packaging>
 
     <name>assira</name>

+ 17 - 23
src/main/java/net/ranides/assira/config/AbstractConfiguration.java

@@ -7,8 +7,6 @@
 
 package net.ranides.assira.config;
 
-import java.io.File;
-import java.net.URL;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -29,33 +27,29 @@ import net.ranides.assira.text.StringUtils;
 })
 public abstract class AbstractConfiguration extends EventRouterDispatcher implements Configuration {
     
-    protected final String name;
-    protected final File file;
-    protected final URL url;
-    protected Map<Object, Object> values;
-    
-    public AbstractConfiguration(String name) {
-        this(name, null, null);
-    }
+    protected final String path;
+    protected final String protocol;
+    protected final String location;
 
-    public AbstractConfiguration(String name, File file) {
-        this(name, file, null);
-    }
-    
-    public AbstractConfiguration(String name, URL url) {
-        this(name, null, url);
-    }
+    protected Map<Object, Object> values;
     
-    private AbstractConfiguration(String name, File file, URL url) {
-        this.name   = name;
-        this.file   = file;
-        this.url    = url;
+    public AbstractConfiguration(String path) {
         this.values = new HashMap<Object, Object>();
+        this.path = path;
+
+        String[] params = path.split(":",2);
+        if(params.length == 2) {
+            protocol = (params.length == 2) ? params[0] : "file";
+            location = params[1];
+        } else {
+            protocol = "file";
+            location = params[0];
+        }
     }
 
     @Override
-    public String name() {
-        return name;
+    public String path() {
+        return path;
     }
     
     @Override

+ 0 - 44
src/main/java/net/ranides/assira/config/ConfigFactory.java

@@ -1,44 +0,0 @@
-/*
- *  @author Ranides Atterwim <ranides@gmail.com>
- *  @copyright Ranides Atterwim
- *  @license WTFPL
- *  @url http://ranides.net/projects/assira
- */
-
-package net.ranides.assira.config;
-
-import java.io.File;
-import java.net.URL;
-
-/**
- * Fabryka konfiguracji - interfejs używany przez {@link ConfigManager}.
- * @see ConfigManager
- * @author ranides
- */
-public interface ConfigFactory {
-    
-    String getName();
-    
-    /**
-     * Tworzy nową instancję fabryki o podanej nazwie
-     * @param name
-     * @return
-     */
-    Configuration getInstance(String name);
-    
-    /**
-     * Tworzy nową instancję fabryki o podanej nazwie
-     * @param name
-     * @return
-     * @todo (ranides) wypieprzyć te file i url. Niech to jakoś sobie fabryka z nazwy wyciąga... ;o
-     */
-    Configuration getInstance(String name, File file);
-    
-    /**
-     * Tworzy nową instancję fabryki o podanej nazwie
-     * @param name
-     * @return
-     */
-    Configuration getInstance(String name, URL url);
-    
-}

+ 0 - 146
src/main/java/net/ranides/assira/config/ConfigManager.java

@@ -1,146 +0,0 @@
-/*
- *  @author Ranides Atterwim <ranides@gmail.com>
- *  @copyright Ranides Atterwim
- *  @license WTFPL
- *  @url http://ranides.net/projects/assira
- */
-
-package net.ranides.assira.config;
-
-import java.io.File;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.ServiceLoader;
-import java.util.WeakHashMap;
-import net.ranides.assira.collection.map.Cache;
-
-/**
- * Klasa zarządzająca cyklem wyszukiwania, ładowania i dostępem do poszczególnych
- * konfiguracji. Dzięki menagerowi nie musimy w aplikacji tworzyć globalnie
- * dostępnych instancji - każdy moduł może zażądać udostępnienia odpowiedniej 
- * konfiguracji na podstawie typu oraz abstrakcyjnej nazwy.
- * 
- * <p>
- * Klasa zarządza również zarejestrowanymi fabrykami, obsługującymi różne formaty
- * konfiguracyjne.
- * </p>
- * @author ranides
- */
-public final class ConfigManager {
-
-    private ConfigManager() { /* utility class */ }
-    
-    private static final Cache<String, ConfigFactory> FACTORIES = new Cache<String, ConfigFactory>();
-    
-    private static final Map<String, Configuration> content = Collections.synchronizedMap(new WeakHashMap<String, Configuration>());
-    
-    private static final Map<String, URL> urls = Collections.synchronizedMap(new WeakHashMap<String, URL>());
-    
-    private static final Map<String, File> files = Collections.synchronizedMap(new WeakHashMap<String, File>());
-    
-    /**
-     * Zwraca konfigurację o podanej nazwie oraz typie. 
-     * Zwrócona konfiguracja jest załadowana. Jeśli wskazana konfiguracja została już
-     * załadowana do pamięci wcześnej, operacja nie jest powtarzana. Metoda może być
-     * wywoływana wielokrotnie bez negatywnego wpływu na wydajność.
-     * @param fqname "factory:name"
-     * @return 
-     */
-    public static Configuration getInstance(String fqname) {
-        String[] params = fqname.split("[ ]*:[ ]*"); 
-        if(params.length != 2) {
-            throw new IllegalArgumentException("Illegal name of configuration: " + fqname);
-        }
-        return getInstance(params[0], params[1]);
-    }
-    
-    /**
-     * Zwraca konfigurację o podanej nazwie oraz typie. 
-     * Zwrócona konfiguracja jest załadowana. Jeśli wskazana konfiguracja została już
-     * załadowana do pamięci wcześnej, operacja nie jest powtarzana. Metoda może być
-     * wywoływana wielokrotnie bez negatywnego wpływu na wydajność.
-     * @param factory
-     * @param name
-     * @return
-     */
-    private static Configuration getInstance(String factory, String name) {
-        String key = factory + ":" + name;
-        if( !content.containsKey(key) ) {
-            Configuration value;
-            if(urls.containsKey(key)) {
-                value = getFactory(factory).getInstance(name, urls.get(key));
-            } else
-            if(files.containsKey(key)) {
-                value = getFactory(factory).getInstance(name, files.get(key));
-            } else {
-                value = getFactory(factory).getInstance(name);
-            }
-            content.put(key, value.load() );
-            return value;
-        }
-        return content.get(name);
-    }
-    
-    private static ConfigFactory getFactory(String name) {
-        if( FACTORIES.containsKey(name) ) {
-            return FACTORIES.get(name);
-        }
-        for(ConfigFactory item : ServiceLoader.load(ConfigFactory.class) ) {
-            if( name.equals(item.getName()) ) {
-                FACTORIES.put(name, item);
-                return item;
-            }
-        }
-        throw new ConfigException("ConfigFactory not found: " + name);
-    }
-    
-    /**
-     * Zwraca listę konfiguracji przyporządkowanych do danej fabryki.
-     * @param factory
-     * @return
-     */
-    public static List<String> getAvailableNames(String factory) {
-        final String prefix = factory+":";
-        final List<String> result = new ArrayList<String>(16);
-        for(String key : urls.keySet()) {
-            if(key.startsWith(prefix)) { result.add(key); }
-        }
-        for(String key : files.keySet()) {
-            if(key.startsWith(prefix)) { result.add(key); }
-        }
-        return result;
-    }
-    
-    /**
-     * Rejestruje nową fabrykę konfiguracji.
-     * @param name
-     * @param factory
-     */
-    public static void registerFactory(String name, ConfigFactory factory) {
-        FACTORIES.put(name, factory);
-    }
-    
-    public static void bind(String fqname, File file) {
-        if(urls.containsKey(fqname)) {
-            throw new IllegalStateException(fqname + " is already binded to URI: " + urls.get(fqname));
-        }
-        if(files.containsKey(fqname)) {
-            throw new IllegalStateException(fqname + " is already binded to file: " + files.get(fqname));
-        }
-        files.put(fqname, file);
-    }
-    
-    public static void bind(String fqname, URL url) {
-        if(urls.containsKey(fqname)) {
-            throw new IllegalStateException(fqname + " is already binded to URI: " + urls.get(fqname));
-        }
-        if(files.containsKey(fqname)) {
-            throw new IllegalStateException(fqname + " is already binded to file: " + files.get(fqname));
-        }
-        urls.put(fqname, url);
-    }
-   
-}

+ 25 - 0
src/main/java/net/ranides/assira/config/ConfigProvider.java

@@ -0,0 +1,25 @@
+/*
+ *  @author Ranides Atterwim <ranides@gmail.com>
+ *  @copyright Ranides Atterwim
+ *  @license WTFPL
+ *  @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.config;
+
+import java.util.Collection;
+
+/**
+ * Fabryka konfiguracji - interfejs używany przez {@link ConfigManager}.
+ * @see ConfigManager
+ * @author ranides
+ */
+public interface ConfigProvider {
+    
+    String getName();
+    
+    Configuration getInstance(String path);
+    
+    Collection<String> list();
+    
+}

+ 74 - 0
src/main/java/net/ranides/assira/config/ConfigService.java

@@ -0,0 +1,74 @@
+/*
+ *  @author Ranides Atterwim <ranides@gmail.com>
+ *  @copyright Ranides Atterwim
+ *  @license WTFPL
+ *  @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.config;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.WeakHashMap;
+import net.ranides.assira.collection.map.Cache;
+
+/**
+ * Klasa zarządzająca cyklem wyszukiwania, ładowania i dostępem do poszczególnych
+ * konfiguracji. Dzięki menagerowi nie musimy w aplikacji tworzyć globalnie
+ * dostępnych instancji - każdy moduł może zażądać udostępnienia odpowiedniej 
+ * konfiguracji na podstawie typu oraz abstrakcyjnej nazwy.
+ * 
+ * <p>
+ * Klasa zarządza również zarejestrowanymi fabrykami, obsługującymi różne formaty
+ * konfiguracyjne.
+ * </p>
+ * @author ranides
+ */
+public final class ConfigService {
+
+    private ConfigService() { /* utility class */ }
+    
+    private static final Cache<String, ConfigProvider> FACTORIES = new Cache<String, ConfigProvider>();
+    
+    private static final Map<String, Configuration> CONFIGS = Collections.synchronizedMap(new WeakHashMap<String, Configuration>());
+    
+    /**
+     * Zwraca konfigurację o podanej nazwie oraz typie. 
+     * Zwrócona konfiguracja jest załadowana. Jeśli wskazana konfiguracja została już
+     * załadowana do pamięci wcześnej, operacja nie jest powtarzana. Metoda może być
+     * wywoływana wielokrotnie bez negatywnego wpływu na wydajność.
+     * @param path "provider:path"
+     * @return 
+     */
+    public static Configuration getInstance(String path) {
+        if( CONFIGS.containsKey(path) ) {
+            return CONFIGS.get(path);
+        }
+        String[] params = path.split(":", 2); 
+        if(params.length != 2) {
+            throw new IllegalArgumentException("Illegal name of configuration: " + path);
+        }
+        Configuration result = getProvider(params[0]).getInstance(params[1]);
+        CONFIGS.put(path, result);
+        return result.load();
+    }
+
+    public static ConfigProvider getProvider(String name) {
+        if( FACTORIES.containsKey(name) ) {
+            return FACTORIES.get(name);
+        }
+        for(ConfigProvider item : ServiceLoader.load(ConfigProvider.class) ) {
+            if( name.equals(item.getName()) ) {
+                FACTORIES.put(name, item);
+                return item;
+            }
+        }
+        throw new ConfigException("ConfigFactory not found: " + name);
+    }
+    
+    public static void register(ConfigProvider provider) {
+        FACTORIES.put(provider.getName(), provider);
+    }
+   
+}

+ 2 - 2
src/main/java/net/ranides/assira/config/Configuration.java

@@ -26,10 +26,10 @@ import net.ranides.assira.events.EventRouter;
 public interface Configuration extends EventRouter {
     
     /**
-     * Abstrakcyjna nazwa załadowanej konfiguracji
+     * Abstrakcyjna ścieżka do załadowanej konfiguracji
      * @return
      */
-    String name();
+    String path();
     
     /**
      * Ładuje konfigurację z persistent storage

+ 12 - 40
src/main/java/net/ranides/assira/config/JSONConfiguration.java

@@ -24,33 +24,25 @@ import java.util.Map;
 public class JSONConfiguration extends AbstractConfiguration {
     
     private static final ObjectMapper mapper;
-    
+        
     static {
         mapper = new ObjectMapper();
         mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
     }
 
-    public JSONConfiguration(String name) {
-        super(name);
-    }
-    
-    public JSONConfiguration(String name, File file) {
-        super(name, file);
-    }
-
-    public JSONConfiguration(String name, URL uri) {
-        super(name, uri);
+    public JSONConfiguration(String path) {
+        super(path);
     }
 
     @Override
     public Configuration load() throws ConfigException {
         try {
-            if( file != null) {
-                values = mapper.readValue(file, new TypeReference<Map<Object,Object>>(){});
+            if("file".equals(protocol)) {
+                values = mapper.readValue(new File(location), new TypeReference<Map<Object,Object>>(){});
                 return this;
             }
-            if( url != null) {
-                values = mapper.readValue(url, new TypeReference<Map<Object,Object>>(){});
+            if("url".equals(protocol)) {
+                values = mapper.readValue(new URL(location), new TypeReference<Map<Object,Object>>(){});
                 return this;
             }
         } catch(IOException cause) {
@@ -61,35 +53,15 @@ public class JSONConfiguration extends AbstractConfiguration {
 
     @Override
     public Configuration save() throws ConfigException {
-        if( file == null) {
-            throw new UnsupportedOperationException("You can save configuration to files only.");
-        }
         try {
-            mapper.writeValue(file, values);
-            return this;
+            if("file".equals(protocol)) {
+                mapper.writeValue(new File(location), values);
+                return this;
+            }
         } catch(IOException cause) {
             throw new ConfigException(cause);
         }
-    }
-    
-    // @todo (ranides) to też jest popsute...
-    public static abstract class Factory implements ConfigFactory {
-
-        @Override
-        public Configuration getInstance(String name) {
-            return new JSONConfiguration(name);
-        }
-
-        @Override
-        public Configuration getInstance(String name, File file) {
-            return new JSONConfiguration(name, file);
-        }
-
-        @Override
-        public Configuration getInstance(String name, URL url) {
-            return new JSONConfiguration(name, url);
-        }
-        
+        throw new ConfigException("You can save configuration to files only.");
     }
     
 }

+ 4 - 10
src/test/java/net/ranides/assira/config/JSONConfigurationTest.java

@@ -21,19 +21,14 @@ public class JSONConfigurationTest {
     
     public JSONConfigurationTest() {
     }
+    
+    
 
     @Test
     public void testSimpleRW() throws IOException {
         File file = FileHelper.createTempFile(".example.json", JSONConfiguration.class);
-        ConfigManager.registerFactory("test", new JSONConfiguration.Factory(){
-            @Override
-            public String getName() {
-                return "test";
-            }
-        });
-        ConfigManager.bind("test:example", file);
         
-        Configuration source = new JSONConfiguration("example", file);
+        Configuration source = new JSONConfiguration("file:" + file);
         
         source.putString("author.name", "ranides");
         source.putString("author.mail", "ranides@gmail.com");
@@ -51,8 +46,7 @@ public class JSONConfigurationTest {
 
         source.save();
         
-        
-        Configuration target =  ConfigManager.getInstance("temp:example"); // new JSONConfiguration("target", file);
+        Configuration target = new JSONConfiguration("file:" + file).load(); // ConfigService.getInstance("temp:file:"+file);
 
         assertEquals(2, ((Map)target.get("author")).size() );
         assertEquals("ranides", target.getString("author.name"));