You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by an...@apache.org on 2016/12/22 21:19:32 UTC

[2/4] incubator-tamaya-sandbox git commit: TAMAYA-145: Added support for Refreshable and property source level filtering.

TAMAYA-145: Added support for Refreshable and property source level filtering.


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

Branch: refs/heads/master
Commit: 20c558d0771a07da54fe26a9a8f28b80cb06014e
Parents: 7b9971d
Author: anatole <an...@apache.org>
Authored: Wed Dec 21 16:58:40 2016 +0100
Committer: anatole <an...@apache.org>
Committed: Wed Dec 21 16:58:40 2016 +0100

----------------------------------------------------------------------
 .../org/apache/tamaya/metamodel/Enabled.java    |  37 +++++
 .../tamaya/metamodel/EnabledPropertySource.java | 135 +++++++++++++++++++
 .../EnabledPropertySourceProvider.java          | 112 +++++++++++++++
 .../apache/tamaya/metamodel/MetaContext.java    |  10 ++
 .../metamodel/internal/ContextReader.java       |   4 +-
 .../DSLLoadingConfigurationProviderSpi.java     |  10 +-
 .../internal/PropertySourceReader.java          |  78 +++++++++++
 7 files changed, 375 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/20c558d0/metamodel/src/main/java/org/apache/tamaya/metamodel/Enabled.java
----------------------------------------------------------------------
diff --git a/metamodel/src/main/java/org/apache/tamaya/metamodel/Enabled.java b/metamodel/src/main/java/org/apache/tamaya/metamodel/Enabled.java
new file mode 100644
index 0000000..b3051d3
--- /dev/null
+++ b/metamodel/src/main/java/org/apache/tamaya/metamodel/Enabled.java
@@ -0,0 +1,37 @@
+/*
+ * 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.tamaya.metamodel;
+
+/**
+ * Common interface for items that can be enabled or disabled.
+ */
+public interface Enabled {
+
+    /**
+     * Returns the enabled property.
+     * @return the enabled value.
+     */
+    boolean isEnabled();
+
+    /**
+     * Enables/disables this property source.
+     * @param enabled the enabled value.
+     */
+    void setEnabled(boolean enabled);
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/20c558d0/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySource.java
----------------------------------------------------------------------
diff --git a/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySource.java b/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySource.java
new file mode 100644
index 0000000..e231930
--- /dev/null
+++ b/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySource.java
@@ -0,0 +1,135 @@
+/*
+ * 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.tamaya.metamodel;
+
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.PropertyValue;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.logging.Logger;
+
+
+/**
+ * Wrapped property source that allows enabling a property source using an
+ * {@code enabled} expression.
+ */
+public final class EnabledPropertySource
+        implements PropertySource, Enabled {
+
+    private static final Logger LOG = Logger.getLogger(EnabledPropertySource.class.getName());
+    private String enabledExpression;
+    private PropertySource wrapped;
+    private boolean enabled;
+
+    public EnabledPropertySource(PropertySource wrapped, Map<String,String> context, String expression) {
+        this.enabledExpression = Objects.requireNonNull(expression);
+        this.wrapped = Objects.requireNonNull(wrapped);
+        this.enabled = calculateEnabled(context);
+    }
+
+    protected boolean calculateEnabled(Map<String, String> context) {
+        try {
+            ScriptEngineManager manager = new ScriptEngineManager();
+            ScriptEngine engine = manager.getEngineByName("nashorn");
+            if(engine==null){
+                engine = manager.getEngineByName("rhino");
+            }
+            // init script engine
+            for(Map.Entry<String,String> entry: context.entrySet()) {
+                engine.put(entry.getKey(), entry.getValue());
+            }
+            Object o = engine.eval(enabledExpression);
+            if(!(o instanceof Boolean)){
+                LOG.severe("Enabled expression must evaluate to Boolean: '"
+                        +enabledExpression+"', but was " + o +
+                        ", property source will be disabled: " +
+                        wrapped.getName());
+                return false;
+            }
+            return (Boolean)o;
+        } catch (ScriptException e) {
+            LOG.severe("Invalid Boolean expression: '"
+                    +enabledExpression+"': " + e + ", property source will be disabled: " +
+                    wrapped.getName());
+        }
+        return false;
+    }
+
+    /**
+     * Returns the enabled property.
+     * @return the enabled value.
+     */
+    @Override
+    public boolean isEnabled(){
+        return enabled;
+    }
+
+    /**
+     * Enables/disables this property source.
+     * @param enabled the enabled value.
+     */
+    @Override
+    public void setEnabled(boolean enabled){
+        this.enabled = enabled;
+    }
+
+    @Override
+    public int getOrdinal() {
+        return this.wrapped.getOrdinal();
+    }
+
+    @Override
+    public String getName() {
+        return this.wrapped.getName();
+    }
+
+    @Override
+    public PropertyValue get(String key) {
+        if(!isEnabled()){
+            return null;
+        }
+        return this.wrapped.get(key);
+    }
+
+    @Override
+    public Map<String, String> getProperties() {
+        if(!isEnabled()){
+            return Collections.emptyMap();
+        }
+        return this.wrapped.getProperties();
+    }
+
+    @Override
+    public boolean isScannable() {
+        return this.wrapped.isScannable();
+    }
+
+    @Override
+    public String toString() {
+        return "DynamicPropertySource{" +
+                "\n enabled=" + enabledExpression +
+                "\n wrapped=" + wrapped +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/20c558d0/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySourceProvider.java
----------------------------------------------------------------------
diff --git a/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySourceProvider.java b/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySourceProvider.java
new file mode 100644
index 0000000..5dfed10
--- /dev/null
+++ b/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySourceProvider.java
@@ -0,0 +1,112 @@
+/*
+ * 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.tamaya.metamodel;
+
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.PropertySourceProvider;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.logging.Logger;
+
+/**
+ * Wrapped property source provider that allows enabling a property source using an
+ * {@code enabled} expression.
+ */
+public final class EnabledPropertySourceProvider
+        implements PropertySourceProvider, Enabled {
+
+    private static final Logger LOG = Logger.getLogger(EnabledPropertySourceProvider.class.getName());
+    private String enabledExpression;
+    private PropertySourceProvider wrapped;
+    private boolean enabled;
+
+    public EnabledPropertySourceProvider(PropertySourceProvider wrapped, Map<String,String> context, String expression) {
+        this.enabledExpression = Objects.requireNonNull(expression);
+        this.wrapped = Objects.requireNonNull(wrapped);
+        this.enabled = calculateEnabled(context);
+    }
+
+    protected boolean calculateEnabled(Map<String, String> context) {
+        try {
+            ScriptEngineManager manager = new ScriptEngineManager();
+            ScriptEngine engine = manager.getEngineByName("nashorn");
+            if(engine==null){
+                engine = manager.getEngineByName("rhino");
+            }
+            // init script engine
+            for(Map.Entry<String,String> entry: context.entrySet()) {
+                engine.put(entry.getKey(), entry.getValue());
+            }
+            Object o = engine.eval(enabledExpression);
+            if(!(o instanceof Boolean)){
+                LOG.severe("Enabled expression must evaluate to Boolean: '"
+                        +enabledExpression+"', but was " + o +
+                        ", property source provider will be disabled: " +
+                        wrapped.getClass().getName());
+                return false;
+            }
+            return (Boolean)o;
+        } catch (ScriptException e) {
+            LOG.severe("Invalid Boolean expression: '"
+                    +enabledExpression+"': " + e + ", property source provider will be disabled: " +
+                    wrapped.getClass().getName());
+        }
+        return false;
+    }
+
+    /**
+     * Returns the enabled property.
+     * @return the enabled value.
+     */
+    @Override
+    public boolean isEnabled(){
+        return enabled;
+    }
+
+    /**
+     * Enables/disables this property source.
+     * @param enabled the enabled value.
+     */
+    @Override
+    public void setEnabled(boolean enabled){
+        this.enabled = enabled;
+    }
+
+    @Override
+    public Collection<PropertySource> getPropertySources() {
+        if(!isEnabled()){
+            return Collections.emptySet();
+        }
+        return this.wrapped.getPropertySources();
+    }
+
+    @Override
+    public String toString() {
+        return "DynamicPropertySourceProvider{" +
+                "\n enabled=" + enabledExpression +
+                "\n wrapped=" + wrapped +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/20c558d0/metamodel/src/main/java/org/apache/tamaya/metamodel/MetaContext.java
----------------------------------------------------------------------
diff --git a/metamodel/src/main/java/org/apache/tamaya/metamodel/MetaContext.java b/metamodel/src/main/java/org/apache/tamaya/metamodel/MetaContext.java
index 4032c03..c4fa25a 100644
--- a/metamodel/src/main/java/org/apache/tamaya/metamodel/MetaContext.java
+++ b/metamodel/src/main/java/org/apache/tamaya/metamodel/MetaContext.java
@@ -52,6 +52,7 @@ public final class MetaContext {
             return new MetaContext();
         }
     };
+    public static final String DEFAULT_CONTEXT_NAME = "<DEFAULT>";
 
     private String id;
 
@@ -72,6 +73,15 @@ public final class MetaContext {
     }
 
     /**
+     * Access the default context. Contexts are managed as weak references in this class. If no
+     * such context exists, a new instance is created.
+     * @return the context instance, never null.
+     */
+    public static MetaContext getDefaultInstance(){
+        return getInstance(DEFAULT_CONTEXT_NAME);
+    }
+
+    /**
      * Access a context by name. Contexts are managed as weak references in this class. If no
      * such valid context exists, a new instance is created, using the given {@code validSupplier}.
      * @param contextName the context name, not null.

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/20c558d0/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/ContextReader.java
----------------------------------------------------------------------
diff --git a/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/ContextReader.java b/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/ContextReader.java
index 5754e4f..db72f13 100644
--- a/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/ContextReader.java
+++ b/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/ContextReader.java
@@ -39,7 +39,7 @@ public class ContextReader implements MetaConfigurationReader {
     @Override
     public void read(Document document, ConfigurationContextBuilder contextBuilder) {
         NodeList nodeList = document.getDocumentElement().getElementsByTagName("context");
-        String contextName = "DEFAULT";
+        String contextName = null;
         LOG.finer("Reading " + nodeList.getLength() + " meta context entries...");
         for(int i=0;i<nodeList.getLength();i++){
             Node node = nodeList.item(i);
@@ -48,7 +48,7 @@ public class ContextReader implements MetaConfigurationReader {
                 if(nameNode!=null){
                     contextName = nameNode.getTextContent();
                 }
-                MetaContext context = MetaContext.getInstance(contextName);
+                MetaContext context = contextName!=null?MetaContext.getInstance(contextName):MetaContext.getDefaultInstance();
                 NodeList entryNodes = node.getChildNodes();
                 for(int c=0;c<entryNodes.getLength();c++){
                     Node entryNode = entryNodes.item(c);

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/20c558d0/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/DSLLoadingConfigurationProviderSpi.java
----------------------------------------------------------------------
diff --git a/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/DSLLoadingConfigurationProviderSpi.java b/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/DSLLoadingConfigurationProviderSpi.java
index 266ed5a..47ce7f0 100644
--- a/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/DSLLoadingConfigurationProviderSpi.java
+++ b/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/DSLLoadingConfigurationProviderSpi.java
@@ -23,18 +23,10 @@ import org.apache.tamaya.spi.*;
 import org.apache.tamaya.Configuration;
 import org.apache.tamaya.spisupport.DefaultConfiguration;
 import org.apache.tamaya.spisupport.DefaultConfigurationContextBuilder;
-import org.w3c.dom.Document;
-import org.xml.sax.InputSource;
 
 import javax.annotation.Priority;
-import javax.xml.parsers.DocumentBuilderFactory;
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.util.Comparator;
 import java.util.Objects;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 
 /**
  * ConfigurationContext that uses {@link MetaConfiguration} to configure the
@@ -48,7 +40,7 @@ public class DSLLoadingConfigurationProviderSpi implements ConfigurationProvider
 
     @Override
     public ConfigurationContextBuilder getConfigurationContextBuilder() {
-        return ServiceContextManager.getServiceContext().getService(ConfigurationContextBuilder.class);
+        return ServiceContextManager.getServiceContext().create(ConfigurationContextBuilder.class);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/20c558d0/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/PropertySourceReader.java
----------------------------------------------------------------------
diff --git a/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/PropertySourceReader.java b/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/PropertySourceReader.java
index 8f42a3b..b3f107c 100644
--- a/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/PropertySourceReader.java
+++ b/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/PropertySourceReader.java
@@ -18,10 +18,12 @@
  */
 package org.apache.tamaya.metamodel.internal;
 
+import org.apache.tamaya.metamodel.*;
 import org.apache.tamaya.metamodel.spi.ItemFactory;
 import org.apache.tamaya.metamodel.spi.ItemFactoryManager;
 import org.apache.tamaya.metamodel.spi.MetaConfigurationReader;
 import org.apache.tamaya.spi.ConfigurationContextBuilder;
+import org.apache.tamaya.spi.PropertyFilter;
 import org.apache.tamaya.spi.PropertySource;
 import org.apache.tamaya.spi.PropertySourceProvider;
 import org.w3c.dom.Document;
@@ -66,6 +68,7 @@ public class PropertySourceReader implements MetaConfigurationReader{
                         PropertySource ps = sourceFactory.create(params);
                         if(ps!=null) {
                             ComponentConfigurator.configure(ps, params);
+                            ps = decoratePropertySource(ps, contextBuilder, node, params);
                             LOG.finer("Adding configured property source: " + ps.getName());
                             contextBuilder.addPropertySources(ps);
                         }
@@ -84,6 +87,7 @@ public class PropertySourceReader implements MetaConfigurationReader{
                         PropertySourceProvider prov = providerFactory.create(params);
                         if(prov!=null) {
                             ComponentConfigurator.configure(prov, node);
+                            prov = decoratePropertySourceProvider(prov, contextBuilder, node, params);
                             LOG.finer("Adding configured property source provider: " + prov.getClass().getName());
                             contextBuilder.addPropertySources(prov.getPropertySources());
                         }
@@ -100,4 +104,78 @@ public class PropertySourceReader implements MetaConfigurationReader{
         }
     }
 
+    /**
+     * Decorates a property source to be refreshable or filtered.
+     * @param ps the wrapped property source
+     * @param contextBuilder
+     *@param configNode the XML config node
+     * @param params the extracted parameter list   @return the property source to be added to the context.
+     */
+    private PropertySource decoratePropertySource(PropertySource ps, ConfigurationContextBuilder contextBuilder, Node configNode, Map<String, String> params){
+        Node refreshableVal = configNode.getAttributes().getNamedItem("refreshable");
+        if(refreshableVal!=null && Boolean.parseBoolean(refreshableVal.getNodeValue())){
+            if(!(ps instanceof Refreshable)){
+                ps = RefreshablePropertySource.of(params, ps);
+            }
+        }
+        NodeList childNodes = configNode.getChildNodes();
+        for(int i=0;i<childNodes.getLength();i++){
+            Node node = childNodes.item(i);
+            if("filter".equals(node.getNodeName())) {
+                ps = FilteredPropertySource.of(ps);
+                configureFilter((FilteredPropertySource) ps, node);
+            }
+        }
+        Node enabledVal = configNode.getAttributes().getNamedItem("enabled");
+        if(enabledVal!=null){
+            ps = new EnabledPropertySource(ps,
+                    MetaContext.getDefaultInstance().getProperties(),
+                    enabledVal.getNodeValue());
+        }
+        return ps;
+    }
+
+    private void configureFilter(FilteredPropertySource ps, Node filterNode) {
+        try {
+            String type = filterNode.getAttributes().getNamedItem("type").getNodeValue();
+            ItemFactory<PropertyFilter> filterFactory = ItemFactoryManager.getInstance().getFactory(PropertyFilter.class, type);
+            if(filterFactory==null){
+                LOG.severe("No such property filter: " + type);
+                return;
+            }
+            Map<String,String> params = ComponentConfigurator.extractParameters(filterNode);
+            PropertyFilter filter = filterFactory.create(params);
+            if(filter!=null) {
+                ComponentConfigurator.configure(filter, params);
+                LOG.finer("Adding configured property filter: " + filter.getClass().getName());
+                ps.addPropertyFilter(filter);
+            }
+        }catch(Exception e){
+            LOG.log(Level.SEVERE, "Failed to read property filter configuration: " + filterNode, e);
+        }
+    }
+
+    /**
+     * Decorates a property source provider to be refreshable or filtered.
+     * @param prov the property source provider to be wrapped.
+     * @param contextBuilder
+     *@param configNode the XML config node
+     * @param params the extracted parameter list   @return the property source provider to be added to the context.
+     */
+    private PropertySourceProvider decoratePropertySourceProvider(PropertySourceProvider prov, ConfigurationContextBuilder contextBuilder, Node configNode, Map<String, String> params){
+        Node refreshableVal = configNode.getAttributes().getNamedItem("refreshable");
+        if(refreshableVal!=null && Boolean.parseBoolean(refreshableVal.getNodeValue())){
+            if(!(prov instanceof Refreshable)){
+                prov = RefreshablePropertySourceProvider.of(params, prov);
+            }
+        }
+        Node enabledVal = configNode.getAttributes().getNamedItem("enabled");
+        if(enabledVal!=null){
+            prov = new EnabledPropertySourceProvider(prov,
+                    MetaContext.getDefaultInstance().getProperties(),
+                    enabledVal.getNodeValue());
+        }
+        return prov;
+    }
+
 }