refactoring of the config interface towards more reuse in the implementation and...
authorErik Brakkee <erik@brakkee.org>
Mon, 25 Jul 2011 21:09:32 +0000 (23:09 +0200)
committerErik Brakkee <erik@brakkee.org>
Mon, 25 Jul 2011 21:09:32 +0000 (23:09 +0200)
16 files changed:
config/src/main/java/org/wamblee/xmlrouter/config/Config.java [new file with mode: 0644]
config/src/main/java/org/wamblee/xmlrouter/config/DocumentType.java
config/src/main/java/org/wamblee/xmlrouter/config/Filter.java
config/src/main/java/org/wamblee/xmlrouter/config/RouterConfig.java
config/src/main/java/org/wamblee/xmlrouter/config/RouterConfigService.java [new file with mode: 0644]
config/src/main/java/org/wamblee/xmlrouter/config/Transformation.java
impl/src/main/java/org/wamblee/xmlrouter/impl/ConfigImpl.java [new file with mode: 0644]
impl/src/main/java/org/wamblee/xmlrouter/impl/Constants.java
impl/src/main/java/org/wamblee/xmlrouter/impl/RobustDocumentType.java [new file with mode: 0644]
impl/src/main/java/org/wamblee/xmlrouter/impl/RobustFilter.java
impl/src/main/java/org/wamblee/xmlrouter/impl/RobustTransformation.java
impl/src/main/java/org/wamblee/xmlrouter/impl/TransformationPath.java
impl/src/main/java/org/wamblee/xmlrouter/impl/Transformations.java
impl/src/main/java/org/wamblee/xmlrouter/impl/XMLRouter.java
impl/src/test/java/org/wamblee/xmlrouter/impl/RobustDocumentTypeTest.java [new file with mode: 0644]
impl/src/test/java/org/wamblee/xmlrouter/impl/XMLRouterTest.java

diff --git a/config/src/main/java/org/wamblee/xmlrouter/config/Config.java b/config/src/main/java/org/wamblee/xmlrouter/config/Config.java
new file mode 100644 (file)
index 0000000..b90b34c
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2005-2011 the original author or authors.
+ * 
+ * Licensed 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.wamblee.xmlrouter.config;
+
+import java.util.Collection;
+
+import org.wamblee.xmlrouter.common.Id;
+
+/**
+ * Basic configuration interface for managing a set of configuration items of a
+ * given type with unique ids.
+ * 
+ * @author Erik Brakkee
+ * 
+ * @param <T>
+ *            Type for which ids are generated.
+ */
+public interface Config<T> {
+
+    /**
+     * Adds a item
+     * 
+     * @param aT
+     *            item
+     * @return Unique id.
+     */
+    Id<T> add(T aT);
+
+    /**
+     * Removes the item with a given id.
+     * 
+     * @param aId
+     *            Item id.
+     * @return true iff the item was removed.
+     */
+    boolean remove(Id<T> aId);
+
+    /**
+     * @return All available ids.
+     */
+    Collection<Id<T>> ids();
+
+    /**
+     * Gets the item for the given id.
+     * 
+     * @param aId
+     *            Item id.
+     * @return Item, or null if not found.
+     */
+    T get(Id<T> aId);
+
+}
\ No newline at end of file
index 9b04ec6122e69d37956c5c0ef5321db554e76498..4ffc5f83bdc07e1a74c6e4dc86d629a97019c7a4 100644 (file)
  * 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.wamblee.xmlrouter.config;
 
 import javax.xml.transform.dom.DOMSource;
 
+/**
+ * Represents a type of document with methods to check whether it is an instance
+ * of the type and to validate it.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
 public interface DocumentType {
-    
+
     /**
-     * Symbolic name for the document type. 
-     * @return Name. 
+     * Symbolic name for the document type.
+     * 
+     * @return Name.
      */
     String getName();
 
     /**
-     * Checks if a document is of the given type. 
-     * @param aSource Document
-     * @return True iff the document is of the given type. 
+     * Checks if a document is of the given type.
+     * 
+     * @param aSource
+     *            Document
+     * @return True iff the document is of the given type.
      */
     boolean isInstance(DOMSource aSource);
-    
+
     /**
-     * Validates the document. Implementations that do not validate should simply
-     * return true always. 
-     * @param aSource Document.
-     * @return True iff the document is valid. 
+     * Validates the document. Implementations that do not validate should
+     * simply return true always.
+     * 
+     * @param aSource
+     *            Document.
+     * @return True iff the document is valid.
      */
     boolean validate(DOMSource aSource);
 }
index 3f329713a779dbc7e086438447fbcf9e5e0a161c..212c65f90162b0f5ae0507b6c278c7ca5d3ab694 100644 (file)
  * 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.wamblee.xmlrouter.config;
 
 import javax.xml.transform.dom.DOMSource;
 
+/**
+ * Represents a custom filter that is used to determine whether or not a certain
+ * event is allowed or not.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
 public interface Filter {
 
     /**
-     * Determines if a given document will be processed or not. 
-     * @param aSource Source document. 
+     * Determines if a given document will be processed or not.
+     * 
+     * @param aSource
+     *            Source document.
      * @return
      */
     boolean isAllowed(String aDocumentType, DOMSource aSource);
index db69d743feedd1d96f70a7a03bdb2eb5da65bba4..9e53f4995929d585f42889929902e32407fdea72 100644 (file)
@@ -15,9 +15,6 @@
  */
 package org.wamblee.xmlrouter.config;
 
-import java.util.Collection;
-
-import org.wamblee.xmlrouter.common.Id;
 
 /**
  * Configuration API for the XML router.
@@ -28,31 +25,13 @@ public interface RouterConfig {
 
     // Documents
 
-    Id<DocumentType> addDocumentType(DocumentType aType);
-
-    void removeDocumentType(Id<DocumentType> aId);
-
-    Collection<Id<DocumentType>> getDocumentTypes();
-
-    DocumentType getDocumentType(Id<DocumentType> aId);
+    Config<DocumentType> getDocumentTypeConfig();
 
     // Transformations
 
-    Id<Transformation> addTransformation(Transformation aTransformation);
-
-    void removeTransformation(Id<Transformation> aId);
-
-    Collection<Id<Transformation>> getTransformations();
-
-    Transformation getTransformation(Id<Transformation> aId);
+    Config<Transformation> getTransformationConfig();
 
     // Filters
 
-    Id<Filter> addFilter(Filter aFilter);
-
-    void removeFilter(Id<Filter> aId);
-
-    Collection<Id<Filter>> getFilters();
-
-    Filter getFilter(Id<Filter> aId);
+    Config<Filter> getFilterConfig();
 }
diff --git a/config/src/main/java/org/wamblee/xmlrouter/config/RouterConfigService.java b/config/src/main/java/org/wamblee/xmlrouter/config/RouterConfigService.java
new file mode 100644 (file)
index 0000000..baefd94
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2005-2011 the original author or authors.
+ * 
+ * Licensed 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.wamblee.xmlrouter.config;
+
+import org.wamblee.xmlrouter.common.Id;
+
+/**
+ * Router configuration service that provides atomic configuration of the
+ * router. This is the entry point for configuring the router.
+ * 
+ * @author Erik Brakkee
+ */
+public interface RouterConfigService {
+
+    /**
+     * @return New empty configuration.
+     */
+    RouterConfig emptyConfig();
+
+    /**
+     * Applies a given configuration.
+     * 
+     * @param aConfig
+     *            Configuration to use.
+     * @param aOldConfig
+     *            Id of the configuration to replace or null if this is a new
+     *            configuration.
+     * @return Id of the applied configuration.
+     */
+    Id<RouterConfig> apply(RouterConfig aConfig, Id<RouterConfig> aOldConfig);
+
+    /**
+     * Clears the configuration for a given id.
+     * 
+     * @param aConfig
+     *            Configuration id.
+     */
+    void clear(Id<RouterConfig> aConfig);
+}
index e7757a65919d6be003b990d813ed57bab7b33507..9f200845dcbe5811a661072a13c40d27bf7303c7 100644 (file)
  * 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.wamblee.xmlrouter.config;
 
 import javax.xml.transform.dom.DOMSource;
 
+/**
+ * Represents a transformation of a give docuemnt type.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
 public interface Transformation {
-    
+
     /**
      * @return Name for the transformation.
      */
-    String getName(); 
+    String getName();
 
     /**
      * From type that can be transformed.
diff --git a/impl/src/main/java/org/wamblee/xmlrouter/impl/ConfigImpl.java b/impl/src/main/java/org/wamblee/xmlrouter/impl/ConfigImpl.java
new file mode 100644 (file)
index 0000000..789f75c
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2005-2011 the original author or authors.
+ * 
+ * Licensed 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.wamblee.xmlrouter.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.wamblee.xmlrouter.common.Id;
+import org.wamblee.xmlrouter.config.Config;
+
+/**
+ * Default implementation of the {@link Config} interface.
+ * 
+ * @author Erik Brakkee
+ * 
+ * @param <T>
+ */
+public abstract class ConfigImpl<T> implements Config<T> {
+
+    private AtomicLong next;
+    private Map<Id<T>, T> registered;
+
+    /**
+     * Constructs the object.
+     */
+    public ConfigImpl() {
+        next = new AtomicLong(1);
+        registered = new LinkedHashMap<Id<T>, T>();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.wamblee.xmlrouter.config.Config#add(T)
+     */
+    @Override
+    public Id<T> add(T aT) {
+        notNull(aT);
+        long seqno = next.incrementAndGet();
+        Id<T> id = new Id<T>(seqno);
+        registered.put(id, wrap(id, aT));
+        return id;
+    }
+
+    public abstract T wrap(Id<T> aId, T aT);
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.wamblee.xmlrouter.config.Config#remove(org.wamblee.xmlrouter.common
+     * .Id)
+     */
+    @Override
+    public boolean remove(Id<T> aId) {
+        notNull(aId);
+        return registered.remove(aId) != null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.wamblee.xmlrouter.config.Config#ids()
+     */
+    @Override
+    public Collection<Id<T>> ids() {
+        return Collections.unmodifiableCollection(registered.keySet());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.wamblee.xmlrouter.config.Config#get(org.wamblee.xmlrouter.common.Id)
+     */
+    @Override
+    public T get(Id<T> aId) {
+        notNull(aId);
+        return registered.get(aId);
+    }
+
+    private void notNull(T aT) {
+        if (aT == null) {
+            throw new NullPointerException("Object is null");
+        }
+    }
+
+    private void notNull(Id<T> aId) {
+        if (aId == null) {
+            throw new NullPointerException("Id is null");
+        }
+    }
+}
index c8daa2623e3b2d29be10a4533535c05ef0ed4a38..c51c1155ebabf6750b4489b2ec9d9a73035788b8 100644 (file)
  * 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.wamblee.xmlrouter.impl;
 
+/**
+ * Constants used by the router for error situations.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
 public enum Constants {
     UNKNOWN_DOCUMENT_TYPE, UNKNOWN_DESTINATION_NAME, UNKNOWN_NAME
 }
diff --git a/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustDocumentType.java b/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustDocumentType.java
new file mode 100644 (file)
index 0000000..34664e8
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2005-2011 the original author or authors.
+ * 
+ * Licensed 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.wamblee.xmlrouter.impl;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.transform.dom.DOMSource;
+
+import org.wamblee.xmlrouter.common.Id;
+import org.wamblee.xmlrouter.config.DocumentType;
+
+/**
+ * Robust document type provides robustness towards externally provided document
+ * types.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
+public class RobustDocumentType implements DocumentType {
+
+    private static final Logger LOGGER = Logger
+        .getLogger(RobustDocumentType.class.getName());
+
+    private Id<DocumentType> id;
+    private DocumentType type;
+
+    /**
+     * Constructs the wrapper.
+     * 
+     * @param aId
+     *            Id.
+     * @param aType
+     *            Document type to wrap.
+     */
+    public RobustDocumentType(Id<DocumentType> aId, DocumentType aType) {
+        id = aId;
+        type = aType;
+    }
+
+    @Override
+    public String getName() {
+        try {
+            String name = type.getName();
+            if (name == null) {
+                LOGGER.log(Level.WARNING, "Document type " + id +
+                    " returned null for name");
+                return Constants.UNKNOWN_DOCUMENT_TYPE.toString();
+            }
+            return name;
+        } catch (Exception e) {
+            LOGGER.log(Level.WARNING, "Document type " + id +
+                " threw exception", e);
+            return Constants.UNKNOWN_DOCUMENT_TYPE.toString();
+        }
+    }
+
+    @Override
+    public boolean isInstance(DOMSource aSource) {
+        try {
+            return type.isInstance(aSource);
+        } catch (Exception e) {
+            LOGGER.log(Level.WARNING, "Document type " + id +
+                " threw exception", e);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean validate(DOMSource aSource) {
+        try {
+            return type.validate(aSource);
+        } catch (Exception e) {
+            LOGGER.log(Level.WARNING, "Document type " + id +
+                " threw exception", e);
+            return false;
+        }
+    }
+}
index 9f85d28e6f3c6eb9b2f1314d1c1a9fa9bd0cf83c..4bf4cf2393bf94eca6a6d13c5294c2db5545edb0 100644 (file)
@@ -12,7 +12,7 @@
  * 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.wamblee.xmlrouter.impl;
 
 import java.util.logging.Level;
@@ -23,6 +23,12 @@ import javax.xml.transform.dom.DOMSource;
 import org.wamblee.xmlrouter.common.Id;
 import org.wamblee.xmlrouter.config.Filter;
 
+/**
+ * This class provides robustness towards externally supplied filters.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
 public class RobustFilter implements Filter {
 
     private static final Logger LOGGER = Logger.getLogger(RobustFilter.class
@@ -31,6 +37,14 @@ public class RobustFilter implements Filter {
     private Id<Filter> id;
     private Filter filter;
 
+    /**
+     * Constructs the wrapper.
+     * 
+     * @param aId
+     *            Id.
+     * @param aFilter
+     *            Filter to wrap.
+     */
     public RobustFilter(Id<Filter> aId, Filter aFilter) {
         id = aId;
         filter = aFilter;
index d398230ff1a41621fd231b13122fb2106c6ac803..81748e0f9c1c1c3274f1c7b4e6791c54e1b50578 100644 (file)
@@ -12,7 +12,7 @@
  * 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.wamblee.xmlrouter.impl;
 
 import java.util.logging.Level;
@@ -24,6 +24,12 @@ import org.wamblee.xml.XMLDocument;
 import org.wamblee.xmlrouter.common.Id;
 import org.wamblee.xmlrouter.config.Transformation;
 
+/**
+ * This class provides robustness towards externally provided transformations.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
 public class RobustTransformation implements Transformation {
 
     private static final Logger LOGGER = Logger
@@ -32,12 +38,20 @@ public class RobustTransformation implements Transformation {
     private Id<Transformation> id;
     private Transformation transformation;
 
+    /**
+     * Constructs the wrapper.
+     * 
+     * @param aId
+     *            Unique id.
+     * @param aTransformation
+     *            Wrapped transformation.
+     */
     public RobustTransformation(Id<Transformation> aId,
         Transformation aTransformation) {
         id = aId;
         transformation = aTransformation;
     }
-    
+
     @Override
     public String getName() {
         try {
index 359b4b4647ae4e4be02c439692b1d6c29917c59d..230c7fcae4a35d2f59df2784bb62c4ee772bceef 100644 (file)
@@ -12,7 +12,7 @@
  * 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.wamblee.xmlrouter.impl;
 
 import java.util.ArrayList;
@@ -20,24 +20,54 @@ import java.util.List;
 
 import org.wamblee.xmlrouter.config.Transformation;
 
+/**
+ * Transformation path from a source document to a destination.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
 public class TransformationPath {
 
     private List<Transformation> transformations;
 
+    /**
+     * Constructs empty path.
+     */
     public TransformationPath() {
         transformations = new ArrayList<Transformation>();
     }
 
+    /**
+     * Constructs path with single transformation.
+     * 
+     * @param aTransformation
+     *            Single transformation.
+     */
     public TransformationPath(Transformation aTransformation) {
         this();
         transformations.add(aTransformation);
     }
 
+    /**
+     * Constructs path with list of transformations.
+     * 
+     * @param aTransformations
+     *            List of transformations.
+     */
     public TransformationPath(List<Transformation> aTransformations) {
         this();
         transformations.addAll(aTransformations);
     }
 
+    /**
+     * Appends a transormation path to the current path.
+     * 
+     * @param aSequence
+     *            Transformation sequence to append.
+     * @return Appended transformations equence.
+     * @throws Runtime
+     *             exception if the appended transformation would not be valid.
+     */
     public TransformationPath appendPath(TransformationPath aSequence) {
         if (transformations.isEmpty()) {
             return new TransformationPath(aSequence.transformations);
@@ -56,10 +86,16 @@ public class TransformationPath {
         return new TransformationPath(t);
     }
 
+    /**
+     * @return Number of transformations.
+     */
     public int size() {
         return transformations.size();
     }
 
+    /**
+     * @return From type of the path or null if the sequence is empty.
+     */
     public String getFromType() {
         if (transformations.isEmpty()) {
             return null;
@@ -67,6 +103,9 @@ public class TransformationPath {
         return transformations.get(0).getFromType();
     }
 
+    /**
+     * @return To type of the path or null if the sequence is empty.
+     */
     public String getToType() {
         if (transformations.isEmpty()) {
             return null;
@@ -74,6 +113,9 @@ public class TransformationPath {
         return transformations.get(transformations.size() - 1).getToType();
     }
 
+    /**
+     * @return The transformations.
+     */
     public List<Transformation> getTransformations() {
         return transformations;
     }
index 7fd3e10e7041efd6173dcc35832b94e67d0cbc46..c297fac787ce5ea66ca66339435fdd85bc59a8c7 100644 (file)
@@ -17,41 +17,89 @@ package org.wamblee.xmlrouter.impl;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashSet;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
 
 import org.wamblee.xmlrouter.common.Id;
+import org.wamblee.xmlrouter.config.Config;
 import org.wamblee.xmlrouter.config.Transformation;
 
+/**
+ * This class manages transformations and computes the shortest transformations
+ * paths based on the provided transformations.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
 public class Transformations {
 
-    private AtomicLong sequenceNumber;
-    private Map<Id<Transformation>, Transformation> transformations;
+    private Config<Transformation> transformations;
     private List<String> vertices;
     private TransformationPath[][] matrix;
 
     private Map<String, List<TransformationPath>> sequences;
 
+    /**
+     * Construct the transformations.
+     */
     public Transformations() {
-        sequenceNumber = new AtomicLong(1);
-        transformations = new LinkedHashMap<Id<Transformation>, Transformation>();
+        transformations = new ConfigImpl<Transformation>() {
+            @Override
+            public Transformation wrap(Id<Transformation> aId,
+                Transformation aType) {
+                return new RobustTransformation(aId, aType);
+            }
+        };
         vertices = new ArrayList<String>();
         matrix = new TransformationPath[0][0];
     }
 
+    public Config<Transformation> getTransformationConfig() {
+        return new Config<Transformation>() {
+            @Override
+            public Id<Transformation> add(Transformation aT) {
+                return addTransformation(aT);
+            }
+
+            @Override
+            public Transformation get(Id<Transformation> aId) {
+                return transformations.get(aId);
+            }
+
+            @Override
+            public Collection<Id<Transformation>> ids() {
+                return transformations.ids();
+            }
+
+            @Override
+            public boolean remove(Id<Transformation> aId) {
+                return transformations.remove(aId);
+            }
+        };
+    }
+
+    /**
+     * Adds a transformation. Leads to recomputation of shortest paths.
+     * 
+     * @param aTransformation
+     *            Transformation to add.
+     * @return Id of the transformation.
+     */
     public Id<Transformation> addTransformation(Transformation aTransformation) {
-        long seqno = sequenceNumber.getAndIncrement();
-        Id<Transformation> id = new Id<Transformation>(seqno);
-        transformations.put(id, new RobustTransformation(id, aTransformation));
+        Id<Transformation> id = transformations.add(aTransformation);
         computeTransformationSequences();
         return id;
     }
 
+    /**
+     * Gets the possible target types based on an input type.
+     * 
+     * @param aType
+     *            Input type.
+     * @return Possible target types.
+     */
     public Collection<String> getPossibleTargetTypes(String aType) {
         int index = vertices.indexOf(aType);
         Set<String> res = new HashSet<String>();
@@ -93,12 +141,16 @@ public class Transformations {
         return matrix[i][j];
     }
 
+    /**
+     * Computest the transformation sequences using Floyd's algorithm.
+     */
     private void computeTransformationSequences() {
         vertices = new ArrayList<String>();
 
         // Obtain possible starting points.
         Set<String> v = new HashSet<String>();
-        for (Transformation transformation : transformations.values()) {
+        for (Id<Transformation> id : transformations.ids()) {
+            Transformation transformation = transformations.get(id);
             v.add(transformation.getFromType());
             v.add(transformation.getToType());
         }
@@ -112,7 +164,8 @@ public class Transformations {
         for (int i = 0; i < nvertices; i++) {
             matrix[i][i] = new TransformationPath();
         }
-        for (Transformation transformation : transformations.values()) {
+        for (Id<Transformation> id : transformations.ids()) {
+            Transformation transformation = transformations.get(id);
             int from = vertices.indexOf(transformation.getFromType());
             int to = vertices.indexOf(transformation.getToType());
             TransformationPath path = new TransformationPath(transformation);
@@ -143,19 +196,17 @@ public class Transformations {
             .size();
     }
 
+    /**
+     * Removes a transformation.
+     * 
+     * @param aId
+     *            Id of the transformation.
+     */
     public void removeTransformation(Id<Transformation> aId) {
         transformations.remove(aId);
         computeTransformationSequences();
     }
 
-    public Collection<Id<Transformation>> getTransformations() {
-        return Collections.unmodifiableCollection(transformations.keySet());
-    }
-
-    public Transformation getTransformation(Id<Transformation> aId) {
-        return transformations.get(aId);
-    }
-
     @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
index e86593d274642c3a18500a899e4a9d4b78255103..99b36354f1fa30a21d057b8995793c8b9283dddc 100644 (file)
@@ -17,7 +17,6 @@ package org.wamblee.xmlrouter.impl;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -32,6 +31,7 @@ import javax.xml.transform.dom.DOMSource;
 import org.wamblee.general.Clock;
 import org.wamblee.xml.XMLDocument;
 import org.wamblee.xmlrouter.common.Id;
+import org.wamblee.xmlrouter.config.Config;
 import org.wamblee.xmlrouter.config.DocumentType;
 import org.wamblee.xmlrouter.config.Filter;
 import org.wamblee.xmlrouter.config.RouterConfig;
@@ -42,95 +42,61 @@ import org.wamblee.xmlrouter.publish.Gateway;
 import org.wamblee.xmlrouter.subscribe.Destination;
 import org.wamblee.xmlrouter.subscribe.DestinationRegistry;
 
-// TODO concurrency.
-
+/**
+ * The XML Router.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
 public class XMLRouter implements RouterConfig, Gateway, DestinationRegistry {
 
     private static final Logger LOGGER = Logger.getLogger(XMLRouter.class
         .getName());
 
+    private AtomicLong sequenceNumbers;
     private EventListener listener;
     private Clock clock;
     private AtomicLong nextEventId;
-    private AtomicLong sequenceNumbers;
-    private Map<Id<DocumentType>, DocumentType> documentTypes;
+
+    private Config<DocumentType> documentTypes;
     private Transformations transformations;
-    private Map<Id<Filter>, Filter> filters;
+    private Config<Filter> filters;
     private Map<Id<Destination>, Destination> destinations;
 
     public XMLRouter(Clock aClock, EventListener aListener) {
+        sequenceNumbers = new AtomicLong(1);
         listener = aListener;
         clock = aClock;
         nextEventId = new AtomicLong(clock.currentTimeMillis());
-        sequenceNumbers = new AtomicLong(1);
-        documentTypes = new LinkedHashMap<Id<DocumentType>, DocumentType>();
+        documentTypes = new ConfigImpl<DocumentType>() {
+            @Override
+            public DocumentType wrap(Id<DocumentType> aId, DocumentType aType) {
+                return new RobustDocumentType(aId, aType);
+            }
+        };
         transformations = new Transformations();
-        filters = new LinkedHashMap<Id<Filter>, Filter>();
+        filters = new ConfigImpl<Filter>() {
+            @Override
+            public Filter wrap(Id<Filter> aId, Filter aFilter) {
+                return new RobustFilter(aId, aFilter);
+            }
+        };
         destinations = new LinkedHashMap<Id<Destination>, Destination>();
     }
 
     @Override
-    public Id<DocumentType> addDocumentType(DocumentType aType) {
-        long seqno = sequenceNumbers.getAndIncrement();
-        documentTypes.put(new Id<DocumentType>(seqno), aType);
-        return new Id<DocumentType>(seqno);
-    }
-
-    @Override
-    public void removeDocumentType(Id<DocumentType> aId) {
-        documentTypes.remove(aId);
-    }
-
-    @Override
-    public Collection<Id<DocumentType>> getDocumentTypes() {
-        return Collections.unmodifiableCollection(documentTypes.keySet());
-    }
-
-    @Override
-    public DocumentType getDocumentType(Id<DocumentType> aId) {
-        return documentTypes.get(aId);
-    }
-
-    @Override
-    public Id<Transformation> addTransformation(Transformation aTransformation) {
-        return transformations.addTransformation(aTransformation);
-    }
-
-    @Override
-    public void removeTransformation(Id<Transformation> aId) {
-        transformations.removeTransformation(aId);
-    }
-
-    @Override
-    public Collection<Id<Transformation>> getTransformations() {
-        return transformations.getTransformations();
-    }
-
-    @Override
-    public Transformation getTransformation(Id<Transformation> aId) {
-        return transformations.getTransformation(aId);
-    }
-
-    @Override
-    public Id<Filter> addFilter(Filter aFilter) {
-        long seqno = sequenceNumbers.getAndIncrement();
-        filters.put(new Id<Filter>(seqno), aFilter);
-        return new Id<Filter>(seqno);
-    }
-
-    @Override
-    public void removeFilter(Id<Filter> aId) {
-        filters.remove(aId);
+    public Config<DocumentType> getDocumentTypeConfig() {
+        return documentTypes;
     }
 
     @Override
-    public Collection<Id<Filter>> getFilters() {
-        return Collections.unmodifiableCollection(filters.keySet());
+    public Config<Transformation> getTransformationConfig() {
+        return transformations.getTransformationConfig();
     }
 
     @Override
-    public Filter getFilter(Id<Filter> aId) {
-        return filters.get(aId);
+    public Config<Filter> getFilterConfig() {
+        return filters;
     }
 
     @Override
@@ -243,7 +209,8 @@ public class XMLRouter implements RouterConfig, Gateway, DestinationRegistry {
 
     private boolean isAllowedByFilters(String aType, DOMSource aEvent) {
         boolean allowed = true;
-        for (Filter filter : filters.values()) {
+        for (Id<Filter> id : filters.ids()) {
+            Filter filter = filters.get(id);
             if (!filter.isAllowed(aType, aEvent)) {
                 allowed = false;
             }
@@ -253,7 +220,8 @@ public class XMLRouter implements RouterConfig, Gateway, DestinationRegistry {
 
     private List<String> determineDocumentTypes(DOMSource aEvent) {
         List<String> res = new ArrayList<String>();
-        for (DocumentType type : documentTypes.values()) {
+        for (Id<DocumentType> id : documentTypes.ids()) {
+            DocumentType type = documentTypes.get(id);
             if (type.isInstance(aEvent)) {
                 res.add(type.getName());
             }
diff --git a/impl/src/test/java/org/wamblee/xmlrouter/impl/RobustDocumentTypeTest.java b/impl/src/test/java/org/wamblee/xmlrouter/impl/RobustDocumentTypeTest.java
new file mode 100644 (file)
index 0000000..57289dc
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2005-2011 the original author or authors.
+ * 
+ * Licensed 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.wamblee.xmlrouter.impl;
+
+import static junit.framework.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import javax.xml.transform.dom.DOMSource;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.wamblee.xmlrouter.common.Id;
+import org.wamblee.xmlrouter.config.DocumentType;
+
+public class RobustDocumentTypeTest {
+
+    private DocumentType documentType;
+    private DocumentType robust;
+    private DOMSource source;
+
+    @Before
+    public void setUp() {
+        documentType = mock(DocumentType.class);
+        robust = new RobustDocumentType(new Id<DocumentType>(10), documentType);
+        source = mock(DOMSource.class);
+    }
+
+    @Test
+    public void testNoEzception() {
+        when(documentType.isInstance(any(DOMSource.class))).thenReturn(true);
+        when(documentType.getName()).thenReturn("hello");
+        assertTrue(robust.isInstance(source));
+        assertEquals("hello", robust.getName());
+
+        verify(documentType).isInstance(same(source));
+
+        reset(documentType);
+        when(documentType.isInstance(any(DOMSource.class))).thenReturn(false);
+        assertFalse(robust.isInstance(source));
+        verify(documentType).isInstance(same(source));
+    }
+
+    @Test
+    public void testExceptionInIsInstance() {
+        doThrow(new RuntimeException("bla")).when(documentType).isInstance(
+            any(DOMSource.class));
+        assertFalse(robust.isInstance(source));
+        verify(documentType).isInstance(same(source));
+    }
+
+    @Test
+    public void testExceptionInValidate() {
+        doThrow(new RuntimeException("bla")).when(documentType).validate(
+            any(DOMSource.class));
+        assertFalse(robust.validate(source));
+        verify(documentType).validate(same(source));
+    }
+
+    @Test
+    public void testExceptionInGetName() {
+        doThrow(new RuntimeException("bla")).when(documentType).getName();
+        assertEquals(Constants.UNKNOWN_DOCUMENT_TYPE.toString(),
+            robust.getName());
+    }
+
+    @Test
+    public void testGetNameReturnsNull() {
+        when(documentType.getName()).thenReturn(null);
+        assertEquals(Constants.UNKNOWN_DOCUMENT_TYPE.toString(),
+            robust.getName());
+    }
+}
index 47eb21a9d7226708946e0f0da4b4cb5a62f33a31..9ee2645a58f8029de45cfae296f4685540790f81 100644 (file)
@@ -29,6 +29,7 @@ import org.junit.Test;
 import org.wamblee.general.SystemClock;
 import org.wamblee.xmlrouter.common.Id;
 import org.wamblee.xmlrouter.config.DocumentType;
+import org.wamblee.xmlrouter.config.Filter;
 import org.wamblee.xmlrouter.config.Transformation;
 import org.wamblee.xmlrouter.listener.EventInfo;
 import org.wamblee.xmlrouter.listener.EventListener;
@@ -93,6 +94,29 @@ public class XMLRouterTest {
         verify(listener).notDelivered(any(EventInfo.class));
     }
 
+    @Test
+    public void testMisBehavingDocumentType() {
+        DocumentType type = mock(DocumentType.class);
+        doThrow(new RuntimeException("x")).when(type).isInstance(
+            any(DOMSource.class));
+        router.getDocumentTypeConfig().add(type);
+        router.publish("xx", mock(DOMSource.class));
+        verify(listener).notDelivered(any(EventInfo.class));
+        // no exception should occur.
+    }
+
+    @Test
+    public void testMisBehavingFilter() {
+        registerDocumentType("any");
+        Filter filter = mock(Filter.class);
+        doThrow(new RuntimeException("x")).when(filter).isAllowed(anyString(),
+            any(DOMSource.class));
+        router.getFilterConfig().add(filter);
+        router.publish("xx", mock(DOMSource.class));
+        verify(listener).notDelivered(any(EventInfo.class));
+        // no exception should occur.
+    }
+
     @Test
     public void testOneDestinationNoTransformationSuccess() {
         destinationSpy = registerDestination(true, "any");
@@ -120,14 +144,14 @@ public class XMLRouterTest {
         DocumentType type = mock(DocumentType.class);
         when(type.isInstance(any(DOMSource.class))).thenReturn(true);
         when(type.getName()).thenReturn(aType);
-        Id<DocumentType> typeId = router.addDocumentType(type);
+        Id<DocumentType> typeId = router.getDocumentTypeConfig().add(type);
     }
 
     private void registerDocumentType(String aType, DOMSource aSource) {
         DocumentType type = mock(DocumentType.class);
         when(type.isInstance(same(aSource))).thenReturn(true);
         when(type.getName()).thenReturn(aType);
-        Id<DocumentType> typeId = router.addDocumentType(type);
+        Id<DocumentType> typeId = router.getDocumentTypeConfig().add(type);
     }
 
     private Destination registerDestination(boolean aResult, String... types) {
@@ -245,7 +269,7 @@ public class XMLRouterTest {
         when(transformation.getFromType()).thenReturn("any");
         when(transformation.getToType()).thenReturn("bla");
         when(transformation.transform(same(source1))).thenReturn(source2);
-        router.addTransformation(transformation);
+        router.getTransformationConfig().add(transformation);
 
         Destination destination = mock(Destination.class);
         when(
@@ -268,6 +292,29 @@ public class XMLRouterTest {
         verify(listener).notDelivered(any(EventInfo.class));
     }
 
+    @Test
+    public void testMisbehavingTransformationOneDestination() {
+        registerDocumentType("any");
+        Transformation transformation = mock(Transformation.class);
+        when(transformation.getName()).thenReturn("trans");
+        when(transformation.getFromType()).thenReturn("any");
+        when(transformation.getToType()).thenReturn("bla");
+        doThrow(new RuntimeException("x")).when(transformation).transform(
+            same(source1));
+        router.getTransformationConfig().add(transformation);
+
+        Destination destination = mock(Destination.class);
+        when(
+            destination.chooseFromTargetTypes((Collection<String>) anyObject()))
+            .thenReturn(Arrays.asList("bla"));
+
+        router.registerDestination(destination);
+
+        when(destination.receive(any(DOMSource.class))).thenReturn(true);
+        router.publish("bla", source1);
+        verify(listener).notDelivered(any(EventInfo.class));
+    }
+
     private Transformation createTransformation(String aFrom, String aTo,
         DOMSource aSource, DOMSource aTarget) {
         Transformation transformation = mock(Transformation.class);
@@ -284,7 +331,7 @@ public class XMLRouterTest {
         Transformation transformation = createTransformation("any", "bla",
             source1, null);
 
-        router.addTransformation(transformation);
+        router.getTransformationConfig().add(transformation);
 
         Destination destination = mock(Destination.class);
         when(
@@ -302,7 +349,7 @@ public class XMLRouterTest {
         Transformation transformation2 = createTransformation("any", "bla2",
             source1, source2);
 
-        router.addTransformation(transformation2);
+        router.getTransformationConfig().add(transformation2);
         when(
             destination.chooseFromTargetTypes((Collection<String>) anyObject()))
             .thenReturn(Arrays.asList("bla", "bla2"));
@@ -346,7 +393,7 @@ public class XMLRouterTest {
         registerDocumentType("other", source2);
         Transformation transformation = createTransformation("any", "other",
             source1, source2);
-        router.addTransformation(transformation);
+        router.getTransformationConfig().add(transformation);
 
         router.publish("source", source1);
         verify(listener, times(2)).delivered(any(EventInfo.class),
@@ -364,10 +411,10 @@ public class XMLRouterTest {
 
         Transformation t1 = createTransformation("any", "intermediate",
             source1, source2);
-        router.addTransformation(t1);
+        router.getTransformationConfig().add(t1);
         Transformation t2 = createTransformation("intermediate", "other",
             source2, source3);
-        router.addTransformation(t2);
+        router.getTransformationConfig().add(t2);
 
         router.publish("source", source1);
         verify(listener).delivered(any(EventInfo.class),