You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by ot...@apache.org on 2014/12/22 23:05:54 UTC

incubator-tamaya git commit: adds listener to specific folder

Repository: incubator-tamaya
Updated Branches:
  refs/heads/master 26e7f2e95 -> 22d2d1ad8


adds listener to specific folder


Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/commit/22d2d1ad
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/tree/22d2d1ad
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/diff/22d2d1ad

Branch: refs/heads/master
Commit: 22d2d1ad87cdc38aba43115f946a79b684bd8cf1
Parents: 26e7f2e
Author: otaviojava <ot...@people.apache.org>
Authored: Mon Dec 22 20:00:18 2014 -0200
Committer: otaviojava <ot...@people.apache.org>
Committed: Mon Dec 22 20:03:40 2014 -0200

----------------------------------------------------------------------
 .../internal/config/FileChangeListener.java     | 137 +++++++++++++++++++
 .../internal/config/FileChangeObserver.java     |  13 ++
 .../core/internal/config/FileConfiguration.java |  74 ++++++++++
 .../tamaya/core/internal/config/FileReader.java |  80 +++++++++++
 .../config/FilesPropertiesConfigProvider.java   |  97 +++++++++++++
 .../FilesPropertiesConfigProviderTest.java      |  69 ++++++++++
 .../test/resources/META-INF/config/example.ini  |   2 +-
 .../META-INF/configuration/example.ini          |  18 +++
 .../META-INF/configuration/example.properties   |  20 +++
 .../META-INF/configuration/example.xml          |  23 ++++
 10 files changed, 532 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/22d2d1ad/core/src/main/java/org/apache/tamaya/core/internal/config/FileChangeListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/FileChangeListener.java b/core/src/main/java/org/apache/tamaya/core/internal/config/FileChangeListener.java
new file mode 100644
index 0000000..94e6694
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/config/FileChangeListener.java
@@ -0,0 +1,137 @@
+package org.apache.tamaya.core.internal.config;
+
+import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.log4j.Logger;
+import org.apache.tamaya.ConfigException;
+import org.apache.tamaya.core.spi.ConfigurationProviderSpi;
+
+/**
+ * Class that has the responsibility to watch the folder and then update the {@link ConfigurationProviderSpi}
+ * to update the Configuration from the properties or xml files, another ones will be ignored.
+ * @see FilesPropertiesConfigProvider
+ * This listener will wait to events and wait to one second to watch again.
+ * <p>If new file was created or modified will update from this file.</p>
+ * <p>If a file was removed then the listener will load using all files left.</p>
+ * @author otaviojava
+ */
+class FileChangeListener implements Runnable {
+
+    private static final Logger LOGGER = Logger.getLogger(FileChangeListener.class);
+
+    private WatchService watchService;
+
+    private FileChangeObserver observer;
+
+    private Map<String, String> configurationMap;
+
+    private Path directory;
+
+    private FileReader fileReader = new FileReader();
+
+    public FileChangeListener(FileChangeObserver observer, Map<String, String> mapConfiguration, Path directory) {
+        this.observer = observer;
+        this.configurationMap = mapConfiguration;
+        this.directory = directory;
+        this.watchService = getWatchService();
+
+        if (Objects.nonNull(watchService) && Objects.nonNull(directory)) {
+            try {
+                directory.register(watchService, ENTRY_DELETE, ENTRY_MODIFY,
+                        ENTRY_CREATE);
+            } catch (IOException e) {
+                throw new FileChangeListenerException("An error happened when does try to registry to watch the folder", e);
+            }
+        }
+    }
+
+
+    @Override
+    public void run() {
+        if (Objects.isNull(watchService) || Objects.isNull(directory)) {
+            return;
+        }
+        while (true) {
+            watchFolder();
+        }
+    }
+
+
+    private void watchFolder() {
+        try {
+            WatchKey watckKey = watchService.take();
+            boolean needUpdate = false;
+            for (WatchEvent<?> event : watckKey.pollEvents()) {
+                Path keyDirectory = (Path) watckKey.watchable();
+                if(listenerPath(event, keyDirectory)) {
+                    needUpdate = true;
+                }
+            }
+
+            if (needUpdate) {
+                observer.update(configurationMap);
+            }
+            watckKey.reset();
+            Thread.sleep(1_000L);
+        } catch (Exception e) {
+            throw new FileChangeListenerException("An error happened when does try to watch the folder", e);
+        }
+    }
+
+    private boolean listenerPath(WatchEvent<?> event, Path keyDirectory) {
+        boolean wasModified = false;
+        Path path = keyDirectory.resolve((Path)event.context());
+        if(fileReader.isObservavleFile(path)) {
+
+            if (event.kind() == ENTRY_CREATE || event.kind() == ENTRY_MODIFY) {
+                wasModified = true;
+                configurationMap.putAll(fileReader.runFile(path.toAbsolutePath()));
+                LOGGER.info("An event was detected  in file: " + path.getFileName());
+            }
+
+            if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
+                wasModified = true;
+                configurationMap = fileReader.runFiles(directory);
+                LOGGER.info("A remotion event was detected  in file: " + path.getFileName());
+            }
+
+        } else {
+            LOGGER.info("Ignoring the file: " +  path.getFileName() + " because is not a properties or xml file");
+        }
+        return wasModified;
+    }
+
+    private WatchService getWatchService() {
+        try {
+            FileSystem fileSystem = Paths.get(".").getFileSystem();
+            return fileSystem.newWatchService();
+        } catch (IOException e) {
+            LOGGER.warn("This file System does not supports WatchService", e);
+            return null;
+        }
+
+    }
+
+    class FileChangeListenerException extends ConfigException {
+
+        private static final long serialVersionUID = -8965486770881001513L;
+
+        public FileChangeListenerException(String message, Throwable cause) {
+            super(message, cause);
+        }
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/22d2d1ad/core/src/main/java/org/apache/tamaya/core/internal/config/FileChangeObserver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/FileChangeObserver.java b/core/src/main/java/org/apache/tamaya/core/internal/config/FileChangeObserver.java
new file mode 100644
index 0000000..e4fc95d
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/config/FileChangeObserver.java
@@ -0,0 +1,13 @@
+package org.apache.tamaya.core.internal.config;
+
+import java.util.Map;
+
+/**
+ * Observer to be used in {@link FileChangeListener} to update all configurations and provider.
+ * @author otaviojava
+ */
+interface FileChangeObserver {
+
+    void update(Map<String, String> configurationMap);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/22d2d1ad/core/src/main/java/org/apache/tamaya/core/internal/config/FileConfiguration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/FileConfiguration.java b/core/src/main/java/org/apache/tamaya/core/internal/config/FileConfiguration.java
new file mode 100644
index 0000000..0b5c2c7
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/config/FileConfiguration.java
@@ -0,0 +1,74 @@
+package org.apache.tamaya.core.internal.config;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.apache.tamaya.Configuration;
+import org.apache.tamaya.MetaInfo;
+
+/**
+ * Implementation of Configuration which the information is from xml or properties files.
+ * Once the File modified, it will update automatically by provider.
+ * @see FilesPropertiesConfigProvider
+ * @see FileChangeObserver
+ * @author otaviojava
+ */
+class FileConfiguration implements Configuration, FileChangeObserver {
+
+	private Map<String, String> configurationMap;
+
+	public FileConfiguration(Map<String, String> configurationMap) {
+        this.configurationMap = configurationMap;
+    }
+
+    @Override
+	public Optional<String> get(String key) {
+		return Optional.ofNullable(configurationMap.get(key));
+	}
+
+	@Override
+	public MetaInfo getMetaInfo() {
+		return MetaInfo.of("files.config");
+	}
+
+	@Override
+	public boolean containsKey(String key) {
+		return configurationMap.containsKey(key);
+	}
+
+	@Override
+	public Map<String, String> toMap() {
+		return configurationMap;
+	}
+
+    @Override
+    public void update(Map<String, String> configurationMap) {
+        synchronized (this) {
+            this.configurationMap = configurationMap;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "org.apache.tamaya.core.internal.config.FileConfiguration: " + configurationMap.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(configurationMap);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if(this == obj) {
+            return true;
+        }
+        if(Configuration.class.isInstance(obj)) {
+            Configuration other = Configuration.class.cast(obj);
+            return Objects.equals(configurationMap, other.toMap());
+        }
+
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/22d2d1ad/core/src/main/java/org/apache/tamaya/core/internal/config/FileReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/FileReader.java b/core/src/main/java/org/apache/tamaya/core/internal/config/FileReader.java
new file mode 100644
index 0000000..287ccc4
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/config/FileReader.java
@@ -0,0 +1,80 @@
+package org.apache.tamaya.core.internal.config;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.InvalidPropertiesFormatException;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * Class to read a file and creates a {@link Map} of {@link String}.
+ * The implementation of {@link Map} will {@link HashMap}
+ * @author otaviojava
+ */
+class FileReader {
+
+    private static final String EXTENSIONS_ACCEPTED = "*.{xml,properties}";
+
+    public Map<String, String> runFiles(Path directory) {
+        Properties properties = createProperties(directory);
+        return properties
+                .stringPropertyNames()
+                .stream()
+                .collect(
+                        Collectors.toMap(Function.identity(),
+                                properties::getProperty));
+    }
+
+    public Map<String, String> runFile(Path path) {
+        Properties properties = new Properties();
+        try {
+            loadFile(properties, path);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return properties
+                .stringPropertyNames()
+                .stream()
+                .collect(
+                        Collectors.toMap(Function.identity(),
+                                properties::getProperty));
+    }
+
+    private Properties createProperties(Path directory) {
+        Properties properties = new Properties();
+
+            try {
+                for (Path path : Files.newDirectoryStream(directory, EXTENSIONS_ACCEPTED)) {
+                    loadFile(properties, path);
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        return properties;
+    }
+
+    private void loadFile(Properties properties, Path path) throws IOException,
+            InvalidPropertiesFormatException {
+        if (isXmlExtension(path)) {
+            properties.loadFromXML(Files.newInputStream(path));
+        } else {
+            properties.load(Files.newInputStream(path));
+        }
+}
+
+    private boolean isXmlExtension(Path path) {
+        return path.toString().toLowerCase().endsWith(".xml");
+    }
+
+    private boolean isPropertiesExtension(Path path) {
+        return path.toString().toLowerCase().endsWith(".properties");
+    }
+
+    public boolean isObservavleFile(Path path) {
+        return isPropertiesExtension(path) || isXmlExtension(path);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/22d2d1ad/core/src/main/java/org/apache/tamaya/core/internal/config/FilesPropertiesConfigProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/FilesPropertiesConfigProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/config/FilesPropertiesConfigProvider.java
new file mode 100644
index 0000000..5cd1a93
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/config/FilesPropertiesConfigProvider.java
@@ -0,0 +1,97 @@
+package org.apache.tamaya.core.internal.config;
+
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.tamaya.ConfigException;
+import org.apache.tamaya.Configuration;
+import org.apache.tamaya.core.spi.ConfigurationProviderSpi;
+
+/**
+ *  This implementation run in a folder and once found xml and properties files
+ *  will create the Configuration, when one file is created, deleted or modified the configuration will update
+ *  automatically.
+ * The default folder is META-INF/configuration, but you can change using the absolute path in
+ * "-Dtamaya.configbase" parameter.
+ * @author otaviojava
+ */
+public class FilesPropertiesConfigProvider implements ConfigurationProviderSpi, FileChangeObserver {
+
+    private static final String DEFAULT_CONFIG_NAME = "files.configuration";
+
+    private Map<String, String> configurationMap = Collections.emptyMap();
+
+    private ExecutorService executor = Executors.newSingleThreadExecutor();
+
+    private List<FileChangeObserver> fileChangeObservers = new ArrayList<>();
+
+    public FilesPropertiesConfigProvider() {
+        Path directory = getDirectory();
+        if (Objects.nonNull(directory)) {
+            configurationMap = new FileReader().runFiles(directory);
+            Runnable runnable = new FileChangeListener(this, configurationMap, directory);
+            executor.execute(runnable);
+        } else {
+            executor.shutdown();
+        }
+    }
+
+    @Override
+    public String getConfigName() {
+        return DEFAULT_CONFIG_NAME;
+    }
+
+    private Path getDirectory() {
+            String absolutePath = System.getProperty("tamaya.configbase");
+
+            if(Objects.nonNull(absolutePath)) {
+                Path path = Paths.get(absolutePath);
+                if(Files.isDirectory(path)) {
+                    return path;
+                }
+            }
+
+            URL resource = FilesPropertiesConfigProvider.class.getResource("/META-INF/configuration/");
+            if (Objects.nonNull(resource)) {
+                try {
+                    return Paths.get(resource.toURI());
+                } catch (URISyntaxException e) {
+                    throw new ConfigException("An error to find the directory to watch", e);
+                }
+            }
+            return null;
+    }
+
+
+    @Override
+    public Configuration getConfiguration() {
+      return new FileConfiguration(Collections.unmodifiableMap(configurationMap));
+    }
+
+    @Override
+    public void reload() {
+        Path directory = getDirectory();
+        if (Objects.nonNull(directory)) {
+            configurationMap = new FileReader().runFiles(directory);
+        }
+    }
+
+    @Override
+    public void update(Map<String, String> configurationMap) {
+        synchronized (this) {
+            this.configurationMap = configurationMap;
+            Map<String, String> unmodifiableMap = Collections.unmodifiableMap(configurationMap);
+            fileChangeObservers.forEach(fi -> fi.update(unmodifiableMap));
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/22d2d1ad/core/src/test/java/org/apache/tamaya/core/internal/config/FilesPropertiesConfigProviderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/tamaya/core/internal/config/FilesPropertiesConfigProviderTest.java b/core/src/test/java/org/apache/tamaya/core/internal/config/FilesPropertiesConfigProviderTest.java
new file mode 100644
index 0000000..5aeeec5
--- /dev/null
+++ b/core/src/test/java/org/apache/tamaya/core/internal/config/FilesPropertiesConfigProviderTest.java
@@ -0,0 +1,69 @@
+package org.apache.tamaya.core.internal.config;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.tamaya.Configuration;
+import org.apache.tamaya.core.spi.ConfigurationProviderSpi;
+import org.junit.Before;
+import org.junit.Test;
+
+public class FilesPropertiesConfigProviderTest {
+
+
+	private ConfigurationProviderSpi configurationProvider;
+
+	@Before
+	public void init() throws InterruptedException {
+		configurationProvider = new FilesPropertiesConfigProvider();
+	}
+
+	@Test
+	public void getTest() throws InterruptedException{
+	    Configuration configuration = configurationProvider.getConfiguration();
+	    assertEquals(configuration.get("team").get(), "Bahia");
+	    assertFalse(configuration.get("ignore").isPresent());
+	}
+
+	@Test
+	public void shouldUpdateAsync() throws Exception {
+	    createPropertiesFile("newFile.properties", "language=java");
+	    Configuration configuration = configurationProvider.getConfiguration();
+
+	    Thread.sleep(100L);
+	    assertEquals(configuration.get("language").get(), "java");
+
+	}
+
+	   @Test
+	    public void shoulIgnoreAsync() throws Exception {
+	        createPropertiesFile("newFile.ini", "name=otavio");
+	        Configuration configuration = configurationProvider.getConfiguration();
+
+	        Thread.sleep(100L);
+	        assertFalse(configuration.get("otavio").isPresent());
+
+	    }
+
+    private void createPropertiesFile(String fileName, String context) throws URISyntaxException,
+            FileNotFoundException, IOException {
+        URL resource = FilesPropertiesConfigProviderTest.class.getResource("/META-INF/configuration/");
+	    Path directory = Paths.get(resource.toURI());
+	    File file = new File(directory.toFile(), fileName);
+        try (OutputStream stream = new FileOutputStream(file)) {
+            stream.write(context.getBytes());
+            file.deleteOnExit();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/22d2d1ad/core/src/test/resources/META-INF/config/example.ini
----------------------------------------------------------------------
diff --git a/core/src/test/resources/META-INF/config/example.ini b/core/src/test/resources/META-INF/config/example.ini
index dcbb1bc..95a2b7c 100644
--- a/core/src/test/resources/META-INF/config/example.ini
+++ b/core/src/test/resources/META-INF/config/example.ini
@@ -28,4 +28,4 @@ prename=Peter
 name=Fischbach
 address=Hand Platz
 zip=4556
-location=Unterland
\ No newline at end of file
+location=Unterland

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/22d2d1ad/core/src/test/resources/META-INF/configuration/example.ini
----------------------------------------------------------------------
diff --git a/core/src/test/resources/META-INF/configuration/example.ini b/core/src/test/resources/META-INF/configuration/example.ini
new file mode 100644
index 0000000..e2cc979
--- /dev/null
+++ b/core/src/test/resources/META-INF/configuration/example.ini
@@ -0,0 +1,18 @@
+#
+# 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 current 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.
+ignore=value

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/22d2d1ad/core/src/test/resources/META-INF/configuration/example.properties
----------------------------------------------------------------------
diff --git a/core/src/test/resources/META-INF/configuration/example.properties b/core/src/test/resources/META-INF/configuration/example.properties
new file mode 100644
index 0000000..ed6148f
--- /dev/null
+++ b/core/src/test/resources/META-INF/configuration/example.properties
@@ -0,0 +1,20 @@
+#
+# 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 current 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.
+country=Brazil
+nacionality=Brazilian
+love=poliana
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/22d2d1ad/core/src/test/resources/META-INF/configuration/example.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/META-INF/configuration/example.xml b/core/src/test/resources/META-INF/configuration/example.xml
new file mode 100644
index 0000000..6a3694a
--- /dev/null
+++ b/core/src/test/resources/META-INF/configuration/example.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+#
+# Copyright 2014 Anatole Tresch and other (see authors).
+#
+# you may not use this file except in compliance with the License.
+# You may obtain a copy current 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.
+#
+-->
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties version="1.0">
+    <entry key="twitter.otavio">otaviojava</entry>
+    <entry key="team">Bahia</entry>
+    <entry key="sport">Soccer</entry>
+</properties>
\ No newline at end of file