- Implemented UI improvements: added SystemInfoProvider abstraction in UI 
module.
- Added impls for core, for formats, resources and resolvers.


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

Branch: refs/heads/master
Commit: 0fde5cfdf0a3be408a54bc86786bc2c17bef4693
Parents: 6f36eb4
Author: anatole <anat...@apache.org>
Authored: Wed Jun 29 22:41:43 2016 +0200
Committer: anatole <anat...@apache.org>
Committed: Tue Aug 16 15:51:30 2016 +0200

----------------------------------------------------------------------
 formats/pom.xml                                 |  8 ++-
 .../tamaya/model/spi/ConfigModelReader.java     |  2 +-
 resolver/pom.xml                                |  6 ++
 .../org/apache/tamaya/resolver/Resolver.java    | 12 ++++
 .../internal/DefaultExpressionEvaluator.java    | 11 +--
 .../resolver/spi/ExpressionEvaluator.java       | 11 +++
 resources/pom.xml                               |  6 ++
 .../tamaya/resource/ResourceResolver.java       |  6 ++
 .../internal/ClassPathResourceLocator.java      |  4 ++
 .../internal/DefaultResourceResolver.java       |  7 +-
 .../resource/internal/FileResourceLocator.java  |  5 ++
 .../resource/internal/PathResourceLocator.java  |  5 ++
 .../resource/internal/URLResourceLocator.java   |  5 ++
 .../org/apache/tamaya/ui/views/SystemView.java  | 73 +-------------------
 .../views/TamayaGeneralSystemInfoProvider.java  | 56 +++++++++++++++
 15 files changed, 139 insertions(+), 78 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/formats/pom.xml
----------------------------------------------------------------------
diff --git a/formats/pom.xml b/formats/pom.xml
index baf3666..f85a9d4 100644
--- a/formats/pom.xml
+++ b/formats/pom.xml
@@ -60,7 +60,13 @@ under the License.
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>org.apache.tamaya.ext</groupId>
+            <artifactId>tamaya-ui</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/model/src/main/java/org/apache/tamaya/model/spi/ConfigModelReader.java
----------------------------------------------------------------------
diff --git 
a/model/src/main/java/org/apache/tamaya/model/spi/ConfigModelReader.java 
b/model/src/main/java/org/apache/tamaya/model/spi/ConfigModelReader.java
index 9944132..9eb6e41 100644
--- a/model/src/main/java/org/apache/tamaya/model/spi/ConfigModelReader.java
+++ b/model/src/main/java/org/apache/tamaya/model/spi/ConfigModelReader.java
@@ -50,7 +50,7 @@ public final class ConfigModelReader {
      * @param props the properties to be read
      * @return a collection of config validations.
      */
-    public static Collection<ConfigModel> loadValidations(Map<String,String> 
props) {
+    public static Collection<ConfigModel> loadValidations(String owner, 
Map<String,String> props) {
         List<ConfigModel> result = new ArrayList<>();
         Set<String> itemKeys = new HashSet<>();
         for (Object key : props.keySet()) {

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resolver/pom.xml
----------------------------------------------------------------------
diff --git a/resolver/pom.xml b/resolver/pom.xml
index 596abb7..0a6ee9d 100644
--- a/resolver/pom.xml
+++ b/resolver/pom.xml
@@ -52,6 +52,12 @@ under the License.
             <optional>true</optional>
         </dependency>
         <dependency>
+            <groupId>org.apache.tamaya.ext</groupId>
+            <artifactId>tamaya-ui</artifactId>
+            <version>${project.version}</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
             <groupId>org.hamcrest</groupId>
             <artifactId>java-hamcrest</artifactId>
         </dependency>

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resolver/src/main/java/org/apache/tamaya/resolver/Resolver.java
----------------------------------------------------------------------
diff --git a/resolver/src/main/java/org/apache/tamaya/resolver/Resolver.java 
b/resolver/src/main/java/org/apache/tamaya/resolver/Resolver.java
index cdd93e0..a112f38 100644
--- a/resolver/src/main/java/org/apache/tamaya/resolver/Resolver.java
+++ b/resolver/src/main/java/org/apache/tamaya/resolver/Resolver.java
@@ -18,9 +18,12 @@
  */
 package org.apache.tamaya.resolver;
 
+import org.apache.tamaya.resolver.internal.ConfigResolver;
 import org.apache.tamaya.resolver.spi.ExpressionEvaluator;
 import org.apache.tamaya.spi.ServiceContextManager;
 
+import java.util.Collection;
+
 /**
  * Resolver singleton.
  */
@@ -62,4 +65,13 @@ public final class Resolver {
         return 
ServiceContextManager.getServiceContext().getService(ExpressionEvaluator.class)
                 .evaluateExpression(null, value, maskNotFound);
     }
+
+    /**
+     * Access a collection with the currently registered {@link 
ConfigResolver} instances.
+     * @return the resolvers currently known, never null.
+     */
+    public static Collection<ConfigResolver> getResolvers(){
+        return 
ServiceContextManager.getServiceContext().getService(ExpressionEvaluator.class)
+                .getResolvers();
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resolver/src/main/java/org/apache/tamaya/resolver/internal/DefaultExpressionEvaluator.java
----------------------------------------------------------------------
diff --git 
a/resolver/src/main/java/org/apache/tamaya/resolver/internal/DefaultExpressionEvaluator.java
 
b/resolver/src/main/java/org/apache/tamaya/resolver/internal/DefaultExpressionEvaluator.java
index 983b10b..85fe845 100644
--- 
a/resolver/src/main/java/org/apache/tamaya/resolver/internal/DefaultExpressionEvaluator.java
+++ 
b/resolver/src/main/java/org/apache/tamaya/resolver/internal/DefaultExpressionEvaluator.java
@@ -23,11 +23,7 @@ import org.apache.tamaya.resolver.spi.ExpressionResolver;
 import org.apache.tamaya.spi.ServiceContextManager;
 
 import javax.annotation.Priority;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.StringTokenizer;
+import java.util.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -163,6 +159,11 @@ public class DefaultExpressionEvaluator implements 
ExpressionEvaluator {
         return resolvedValue.toString();
     }
 
+    @Override
+    public Collection<ExpressionResolver> getResolvers() {
+        return new ArrayList<>(this.resolvers);
+    }
+
     /**
      * Parses subexpression from tokenizer, hereby counting all open and 
closed brackets, but ignoring any
      * meta characters.

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resolver/src/main/java/org/apache/tamaya/resolver/spi/ExpressionEvaluator.java
----------------------------------------------------------------------
diff --git 
a/resolver/src/main/java/org/apache/tamaya/resolver/spi/ExpressionEvaluator.java
 
b/resolver/src/main/java/org/apache/tamaya/resolver/spi/ExpressionEvaluator.java
index e9e8aa4..dbbf23f 100644
--- 
a/resolver/src/main/java/org/apache/tamaya/resolver/spi/ExpressionEvaluator.java
+++ 
b/resolver/src/main/java/org/apache/tamaya/resolver/spi/ExpressionEvaluator.java
@@ -19,6 +19,10 @@
 package org.apache.tamaya.resolver.spi;
 
 
+import org.apache.tamaya.resolver.internal.ConfigResolver;
+
+import java.util.Collection;
+
 /**
  * Interface that provides an SPI that can be accessed from the current {@link 
org.apache.tamaya.spi.ServiceContext},
  * which allows to pass expression that contain placeholders and variable 
expressions. Expressions passed hereby
@@ -54,4 +58,11 @@ public interface ExpressionEvaluator {
      * @return the filtered/evaluated value, including null.
      */
     String evaluateExpression(String key, String value, boolean maskNotFound);
+
+    /**
+     * Access a collection with the currently registered {@link 
ConfigResolver} instances.
+     * @return the resolvers currently known, never null.
+     */
+    Collection<ExpressionResolver> getResolvers();
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resources/pom.xml
----------------------------------------------------------------------
diff --git a/resources/pom.xml b/resources/pom.xml
index 1ba7dbe..b99d8b6 100644
--- a/resources/pom.xml
+++ b/resources/pom.xml
@@ -46,6 +46,12 @@ under the License.
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.tamaya.ext</groupId>
+            <artifactId>tamaya-ui</artifactId>
+            <version>${project.version}</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
             <groupId>org.hamcrest</groupId>
             <artifactId>java-hamcrest</artifactId>
         </dependency>

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resources/src/main/java/org/apache/tamaya/resource/ResourceResolver.java
----------------------------------------------------------------------
diff --git 
a/resources/src/main/java/org/apache/tamaya/resource/ResourceResolver.java 
b/resources/src/main/java/org/apache/tamaya/resource/ResourceResolver.java
index 7e9acc8..d27b38f 100644
--- a/resources/src/main/java/org/apache/tamaya/resource/ResourceResolver.java
+++ b/resources/src/main/java/org/apache/tamaya/resource/ResourceResolver.java
@@ -92,4 +92,10 @@ public interface ResourceResolver {
      */
     Collection<URL> getResources(ClassLoader classLoader, Collection<String> 
expressions);
 
+    /**
+     * Access the currently registered {@link ResourceLocator} instances.
+     * @return the currently known {@link ResourceLocator} instances, never 
null.
+     */
+    Collection<ResourceLocator> getResourceLocators();
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resources/src/main/java/org/apache/tamaya/resource/internal/ClassPathResourceLocator.java
----------------------------------------------------------------------
diff --git 
a/resources/src/main/java/org/apache/tamaya/resource/internal/ClassPathResourceLocator.java
 
b/resources/src/main/java/org/apache/tamaya/resource/internal/ClassPathResourceLocator.java
index a07a142..5a49d50 100644
--- 
a/resources/src/main/java/org/apache/tamaya/resource/internal/ClassPathResourceLocator.java
+++ 
b/resources/src/main/java/org/apache/tamaya/resource/internal/ClassPathResourceLocator.java
@@ -62,4 +62,8 @@ public class ClassPathResourceLocator implements 
ResourceLocator{
         }
     }
 
+    @Override
+    public String toString() {
+        return "ClassPathResourceLocator -> classpath:<expression>, e.g. 
classpath:META-INF/services/*Resolver";
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resources/src/main/java/org/apache/tamaya/resource/internal/DefaultResourceResolver.java
----------------------------------------------------------------------
diff --git 
a/resources/src/main/java/org/apache/tamaya/resource/internal/DefaultResourceResolver.java
 
b/resources/src/main/java/org/apache/tamaya/resource/internal/DefaultResourceResolver.java
index 6cdb2e2..99e7b3f 100644
--- 
a/resources/src/main/java/org/apache/tamaya/resource/internal/DefaultResourceResolver.java
+++ 
b/resources/src/main/java/org/apache/tamaya/resource/internal/DefaultResourceResolver.java
@@ -41,7 +41,7 @@ public class DefaultResourceResolver extends 
BaseResourceResolver {
     public List<URL> getResources(ClassLoader classLoader, Collection<String> 
expressions) {
         List<URL> resources = new ArrayList<>();
         for (String expression : expressions) {
-            for(ResourceLocator locator: 
ServiceContextManager.getServiceContext().getServices(ResourceLocator.class)){
+            for(ResourceLocator locator: getResourceLocators()){
                 Collection<URL> found = locator.lookup(classLoader, 
expression);
                 if(!found.isEmpty()) {
                     resources.addAll(found);
@@ -52,4 +52,9 @@ public class DefaultResourceResolver extends 
BaseResourceResolver {
         return resources;
     }
 
+    @Override
+    public Collection<ResourceLocator> getResourceLocators() {
+        return 
ServiceContextManager.getServiceContext().getServices(ResourceLocator.class);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resources/src/main/java/org/apache/tamaya/resource/internal/FileResourceLocator.java
----------------------------------------------------------------------
diff --git 
a/resources/src/main/java/org/apache/tamaya/resource/internal/FileResourceLocator.java
 
b/resources/src/main/java/org/apache/tamaya/resource/internal/FileResourceLocator.java
index e19e732..a58b414 100644
--- 
a/resources/src/main/java/org/apache/tamaya/resource/internal/FileResourceLocator.java
+++ 
b/resources/src/main/java/org/apache/tamaya/resource/internal/FileResourceLocator.java
@@ -61,4 +61,9 @@ public class FileResourceLocator implements ResourceLocator{
         }
     }
 
+    @Override
+    public String toString() {
+        return "FileResourceLocator -> file:<expression>, e.g. 
file:./config/**/*.xml";
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resources/src/main/java/org/apache/tamaya/resource/internal/PathResourceLocator.java
----------------------------------------------------------------------
diff --git 
a/resources/src/main/java/org/apache/tamaya/resource/internal/PathResourceLocator.java
 
b/resources/src/main/java/org/apache/tamaya/resource/internal/PathResourceLocator.java
index 84cc25b..e649e3b 100644
--- 
a/resources/src/main/java/org/apache/tamaya/resource/internal/PathResourceLocator.java
+++ 
b/resources/src/main/java/org/apache/tamaya/resource/internal/PathResourceLocator.java
@@ -57,4 +57,9 @@ public class PathResourceLocator implements ResourceLocator{
         }
     }
 
+    @Override
+    public String toString() {
+        return "PathResourceLocator -> <fileExpression>,<classpathExpression>, 
e.g. /**/*.xml";
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/resources/src/main/java/org/apache/tamaya/resource/internal/URLResourceLocator.java
----------------------------------------------------------------------
diff --git 
a/resources/src/main/java/org/apache/tamaya/resource/internal/URLResourceLocator.java
 
b/resources/src/main/java/org/apache/tamaya/resource/internal/URLResourceLocator.java
index 7a9d012..53ef00a 100644
--- 
a/resources/src/main/java/org/apache/tamaya/resource/internal/URLResourceLocator.java
+++ 
b/resources/src/main/java/org/apache/tamaya/resource/internal/URLResourceLocator.java
@@ -58,4 +58,9 @@ public class URLResourceLocator implements ResourceLocator{
         }
     }
 
+    @Override
+    public String toString() {
+        return "URLResourceLocator -> <url>";
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/ui/src/main/java/org/apache/tamaya/ui/views/SystemView.java
----------------------------------------------------------------------
diff --git a/ui/src/main/java/org/apache/tamaya/ui/views/SystemView.java 
b/ui/src/main/java/org/apache/tamaya/ui/views/SystemView.java
index e980a02..4568162 100644
--- a/ui/src/main/java/org/apache/tamaya/ui/views/SystemView.java
+++ b/ui/src/main/java/org/apache/tamaya/ui/views/SystemView.java
@@ -104,76 +104,9 @@ public class SystemView extends VerticalSpacedLayout 
implements View {
 
     private void fillComponentTree() {
         configTree.removeAllItems();
-        Configuration config = ConfigurationProvider.getConfiguration();
-
-        String currentParent = "General";
-        configTree.addItem(currentParent);
-        configTree.addItem("Configuration.class");
-        configTree.setItemCaption("Configuration.class", "Configuration class 
= " + config.getClass().getName());
-        configTree.setParent("Configuration.class", currentParent);
-        configTree.setChildrenAllowed("Configuration.class", false);
-
-        configTree.addItem("ConfigurationContext.class");
-        configTree.setItemCaption("ConfigurationContext.class", 
"ConfigurationContext class = " +
-                config.getContext().getClass().getName());
-        configTree.setParent("ConfigurationContext.class", currentParent);
-        configTree.setChildrenAllowed("ConfigurationContext.class", false);
-
-        configTree.addItem("PropertyValueCombinationPolicy.class");
-        configTree.setItemCaption("PropertyValueCombinationPolicy.class",
-                PropertyValueCombinationPolicy.class.getSimpleName() + " class 
= " +
-                        
config.getContext().getPropertyValueCombinationPolicy().getClass().getName());
-        configTree.setParent("PropertyValueCombinationPolicy.class", 
currentParent);
-        configTree.setChildrenAllowed("PropertyValueCombinationPolicy.class", 
false);
-
-        configTree.addItem("ConfigurationContext.types");
-        configTree.setItemCaption("ConfigurationContext.types", "Configurable 
types");
-        configTree.setParent("ConfigurationContext.types", currentParent);
-        for(Map.Entry<TypeLiteral<?>,List<PropertyConverter<?>>> 
en:config.getContext().getPropertyConverters().entrySet()){
-            configTree.addItem(en.getKey());
-            configTree.setItemCaption(en.getKey(), "Type = " + 
en.getKey().toString());
-            configTree.setParent(en.getKey(), "ConfigurationContext.types");
-            for(PropertyConverter conv: en.getValue()){
-                configTree.addItem(conv);
-                configTree.setItemCaption(conv, conv.getClass().getName());
-                configTree.setChildrenAllowed(conv, false);
-                configTree.setParent(conv, en.getKey());
-            }
-        }
-        configTree.addItem("ConfigurationContext.filters");
-        configTree.setItemCaption("ConfigurationContext.filters", "Property 
Filters");
-        for(PropertyFilter filter: config.getContext().getPropertyFilters()){
-            configTree.addItem(filter);
-            configTree.setItemCaption(filter, filter.getClass().getName());
-            configTree.setChildrenAllowed(filter, false);
-            configTree.setParent(filter, "ConfigurationContext.filters");
-        }
-        configTree.addItem("ConfigurationContext.sources");
-        configTree.setItemCaption("ConfigurationContext.sources", "Property 
Sources");
-        for(PropertySource source: config.getContext().getPropertySources()){
-            configTree.addItem(source);
-            configTree.setItemCaption(source, "name = "+source.getName());
-            configTree.setParent(source, "ConfigurationContext.sources");
-
-            configTree.addItem(source.toString() + ".ordinal");
-            configTree.setItemCaption(source.toString() + ".ordinal", "ordinal 
= "+source.getOrdinal());
-            configTree.setParent(source.toString() + ".ordinal", source);
-            configTree.setChildrenAllowed(source.toString() + ".ordinal", 
false);
-            configTree.addItem(source.toString() + ".class");
-            configTree.setItemCaption(source.toString() + ".class", "class = 
"+source.getClass().getName());
-            configTree.setChildrenAllowed(source.toString() + ".class", false);
-            configTree.setParent(source.toString() + ".class", source);
-            Map<String,String> props = source.getProperties();
-            configTree.addItem(props);
-            configTree.setItemCaption(props, "properties:");
-            configTree.setParent(props, source);
-            for(Map.Entry propEn:props.entrySet()){
-                String entryKey = props.hashCode() + 
propEn.getKey().toString();
-                configTree.addItem(entryKey);
-                configTree.setChildrenAllowed(entryKey, false);
-                configTree.setItemCaption(entryKey, propEn.getKey() + "=" + 
propEn.getValue());
-                configTree.setParent(entryKey, props);
-            }
+        for(SystemInfoProvider 
infoProvider:ServiceContextManager.getServiceContext()
+                .getServices(SystemInfoProvider.class)){
+            infoProvider.provideSystemInfo(configTree);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/0fde5cfd/ui/src/main/java/org/apache/tamaya/ui/views/TamayaGeneralSystemInfoProvider.java
----------------------------------------------------------------------
diff --git 
a/ui/src/main/java/org/apache/tamaya/ui/views/TamayaGeneralSystemInfoProvider.java
 
b/ui/src/main/java/org/apache/tamaya/ui/views/TamayaGeneralSystemInfoProvider.java
new file mode 100644
index 0000000..fd37136
--- /dev/null
+++ 
b/ui/src/main/java/org/apache/tamaya/ui/views/TamayaGeneralSystemInfoProvider.java
@@ -0,0 +1,56 @@
+/*
+ * 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.ui.views;
+
+import com.vaadin.ui.Tree;
+import org.apache.tamaya.Configuration;
+import org.apache.tamaya.ConfigurationProvider;
+import org.apache.tamaya.spi.PropertyValueCombinationPolicy;
+
+import javax.annotation.Priority;
+
+/**
+ * Created by atsticks on 29.06.16.
+ */
+@Priority(0)
+public class TamayaGeneralSystemInfoProvider implements SystemInfoProvider{
+    @Override
+    public void provideSystemInfo(Tree tree) {
+        Configuration config = ConfigurationProvider.getConfiguration();
+        String currentParent = "General";
+        tree.addItem(currentParent);
+        tree.addItem("Configuration.class");
+        tree.setItemCaption("Configuration.class", "Configuration class = " + 
config.getClass().getName());
+        tree.setParent("Configuration.class", currentParent);
+        tree.setChildrenAllowed("Configuration.class", false);
+
+        tree.addItem("ConfigurationContext.class");
+        tree.setItemCaption("ConfigurationContext.class", 
"ConfigurationContext class = " +
+                config.getContext().getClass().getName());
+        tree.setParent("ConfigurationContext.class", currentParent);
+        tree.setChildrenAllowed("ConfigurationContext.class", false);
+
+        tree.addItem("PropertyValueCombinationPolicy.class");
+        tree.setItemCaption("PropertyValueCombinationPolicy.class",
+                PropertyValueCombinationPolicy.class.getSimpleName() + " class 
= " +
+                        
config.getContext().getPropertyValueCombinationPolicy().getClass().getName());
+        tree.setParent("PropertyValueCombinationPolicy.class", currentParent);
+        tree.setChildrenAllowed("PropertyValueCombinationPolicy.class", false);
+    }
+}

Reply via email to