From b2375f35a2f897e1417e8b5ec5b19b3257a11586 Mon Sep 17 00:00:00 2001 From: Erik Brakkee Date: Mon, 25 Jul 2011 23:09:32 +0200 Subject: [PATCH] refactoring of the config interface towards more reuse in the implementation and atomic configuration. --- .../org/wamblee/xmlrouter/config/Config.java | 65 +++++++++++ .../xmlrouter/config/DocumentType.java | 36 ++++-- .../org/wamblee/xmlrouter/config/Filter.java | 15 ++- .../xmlrouter/config/RouterConfig.java | 27 +---- .../xmlrouter/config/RouterConfigService.java | 52 +++++++++ .../xmlrouter/config/Transformation.java | 12 +- .../wamblee/xmlrouter/impl/ConfigImpl.java | 109 ++++++++++++++++++ .../org/wamblee/xmlrouter/impl/Constants.java | 8 +- .../xmlrouter/impl/RobustDocumentType.java | 92 +++++++++++++++ .../wamblee/xmlrouter/impl/RobustFilter.java | 16 ++- .../xmlrouter/impl/RobustTransformation.java | 18 ++- .../xmlrouter/impl/TransformationPath.java | 44 ++++++- .../xmlrouter/impl/Transformations.java | 91 +++++++++++---- .../org/wamblee/xmlrouter/impl/XMLRouter.java | 100 ++++++---------- .../impl/RobustDocumentTypeTest.java | 86 ++++++++++++++ .../wamblee/xmlrouter/impl/XMLRouterTest.java | 63 ++++++++-- 16 files changed, 693 insertions(+), 141 deletions(-) create mode 100644 config/src/main/java/org/wamblee/xmlrouter/config/Config.java create mode 100644 config/src/main/java/org/wamblee/xmlrouter/config/RouterConfigService.java create mode 100644 impl/src/main/java/org/wamblee/xmlrouter/impl/ConfigImpl.java create mode 100644 impl/src/main/java/org/wamblee/xmlrouter/impl/RobustDocumentType.java create mode 100644 impl/src/test/java/org/wamblee/xmlrouter/impl/RobustDocumentTypeTest.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 index 0000000..b90b34c --- /dev/null +++ b/config/src/main/java/org/wamblee/xmlrouter/config/Config.java @@ -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 + * Type for which ids are generated. + */ +public interface Config { + + /** + * Adds a item + * + * @param aT + * item + * @return Unique id. + */ + Id add(T aT); + + /** + * Removes the item with a given id. + * + * @param aId + * Item id. + * @return true iff the item was removed. + */ + boolean remove(Id aId); + + /** + * @return All available ids. + */ + Collection> ids(); + + /** + * Gets the item for the given id. + * + * @param aId + * Item id. + * @return Item, or null if not found. + */ + T get(Id aId); + +} \ No newline at end of file diff --git a/config/src/main/java/org/wamblee/xmlrouter/config/DocumentType.java b/config/src/main/java/org/wamblee/xmlrouter/config/DocumentType.java index 9b04ec6..4ffc5f8 100644 --- a/config/src/main/java/org/wamblee/xmlrouter/config/DocumentType.java +++ b/config/src/main/java/org/wamblee/xmlrouter/config/DocumentType.java @@ -12,31 +12,43 @@ * 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); } diff --git a/config/src/main/java/org/wamblee/xmlrouter/config/Filter.java b/config/src/main/java/org/wamblee/xmlrouter/config/Filter.java index 3f32971..212c65f 100644 --- a/config/src/main/java/org/wamblee/xmlrouter/config/Filter.java +++ b/config/src/main/java/org/wamblee/xmlrouter/config/Filter.java @@ -12,16 +12,25 @@ * 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); diff --git a/config/src/main/java/org/wamblee/xmlrouter/config/RouterConfig.java b/config/src/main/java/org/wamblee/xmlrouter/config/RouterConfig.java index db69d74..9e53f49 100644 --- a/config/src/main/java/org/wamblee/xmlrouter/config/RouterConfig.java +++ b/config/src/main/java/org/wamblee/xmlrouter/config/RouterConfig.java @@ -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 addDocumentType(DocumentType aType); - - void removeDocumentType(Id aId); - - Collection> getDocumentTypes(); - - DocumentType getDocumentType(Id aId); + Config getDocumentTypeConfig(); // Transformations - Id addTransformation(Transformation aTransformation); - - void removeTransformation(Id aId); - - Collection> getTransformations(); - - Transformation getTransformation(Id aId); + Config getTransformationConfig(); // Filters - Id addFilter(Filter aFilter); - - void removeFilter(Id aId); - - Collection> getFilters(); - - Filter getFilter(Id aId); + Config 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 index 0000000..baefd94 --- /dev/null +++ b/config/src/main/java/org/wamblee/xmlrouter/config/RouterConfigService.java @@ -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 apply(RouterConfig aConfig, Id aOldConfig); + + /** + * Clears the configuration for a given id. + * + * @param aConfig + * Configuration id. + */ + void clear(Id aConfig); +} diff --git a/config/src/main/java/org/wamblee/xmlrouter/config/Transformation.java b/config/src/main/java/org/wamblee/xmlrouter/config/Transformation.java index e7757a6..9f20084 100644 --- a/config/src/main/java/org/wamblee/xmlrouter/config/Transformation.java +++ b/config/src/main/java/org/wamblee/xmlrouter/config/Transformation.java @@ -12,17 +12,23 @@ * 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 index 0000000..789f75c --- /dev/null +++ b/impl/src/main/java/org/wamblee/xmlrouter/impl/ConfigImpl.java @@ -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 + */ +public abstract class ConfigImpl implements Config { + + private AtomicLong next; + private Map, T> registered; + + /** + * Constructs the object. + */ + public ConfigImpl() { + next = new AtomicLong(1); + registered = new LinkedHashMap, T>(); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.xmlrouter.config.Config#add(T) + */ + @Override + public Id add(T aT) { + notNull(aT); + long seqno = next.incrementAndGet(); + Id id = new Id(seqno); + registered.put(id, wrap(id, aT)); + return id; + } + + public abstract T wrap(Id aId, T aT); + + /* + * (non-Javadoc) + * + * @see + * org.wamblee.xmlrouter.config.Config#remove(org.wamblee.xmlrouter.common + * .Id) + */ + @Override + public boolean remove(Id aId) { + notNull(aId); + return registered.remove(aId) != null; + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.xmlrouter.config.Config#ids() + */ + @Override + public Collection> 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 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 aId) { + if (aId == null) { + throw new NullPointerException("Id is null"); + } + } +} diff --git a/impl/src/main/java/org/wamblee/xmlrouter/impl/Constants.java b/impl/src/main/java/org/wamblee/xmlrouter/impl/Constants.java index c8daa26..c51c115 100644 --- a/impl/src/main/java/org/wamblee/xmlrouter/impl/Constants.java +++ b/impl/src/main/java/org/wamblee/xmlrouter/impl/Constants.java @@ -12,9 +12,15 @@ * 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 index 0000000..34664e8 --- /dev/null +++ b/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustDocumentType.java @@ -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 id; + private DocumentType type; + + /** + * Constructs the wrapper. + * + * @param aId + * Id. + * @param aType + * Document type to wrap. + */ + public RobustDocumentType(Id 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; + } + } +} diff --git a/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustFilter.java b/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustFilter.java index 9f85d28..4bf4cf2 100644 --- a/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustFilter.java +++ b/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustFilter.java @@ -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 id; private Filter filter; + /** + * Constructs the wrapper. + * + * @param aId + * Id. + * @param aFilter + * Filter to wrap. + */ public RobustFilter(Id aId, Filter aFilter) { id = aId; filter = aFilter; diff --git a/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustTransformation.java b/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustTransformation.java index d398230..81748e0 100644 --- a/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustTransformation.java +++ b/impl/src/main/java/org/wamblee/xmlrouter/impl/RobustTransformation.java @@ -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 id; private Transformation transformation; + /** + * Constructs the wrapper. + * + * @param aId + * Unique id. + * @param aTransformation + * Wrapped transformation. + */ public RobustTransformation(Id aId, Transformation aTransformation) { id = aId; transformation = aTransformation; } - + @Override public String getName() { try { diff --git a/impl/src/main/java/org/wamblee/xmlrouter/impl/TransformationPath.java b/impl/src/main/java/org/wamblee/xmlrouter/impl/TransformationPath.java index 359b4b4..230c7fc 100644 --- a/impl/src/main/java/org/wamblee/xmlrouter/impl/TransformationPath.java +++ b/impl/src/main/java/org/wamblee/xmlrouter/impl/TransformationPath.java @@ -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 transformations; + /** + * Constructs empty path. + */ public TransformationPath() { transformations = new ArrayList(); } + /** + * 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 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 getTransformations() { return transformations; } diff --git a/impl/src/main/java/org/wamblee/xmlrouter/impl/Transformations.java b/impl/src/main/java/org/wamblee/xmlrouter/impl/Transformations.java index 7fd3e10..c297fac 100644 --- a/impl/src/main/java/org/wamblee/xmlrouter/impl/Transformations.java +++ b/impl/src/main/java/org/wamblee/xmlrouter/impl/Transformations.java @@ -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, Transformation> transformations; + private Config transformations; private List vertices; private TransformationPath[][] matrix; private Map> sequences; + /** + * Construct the transformations. + */ public Transformations() { - sequenceNumber = new AtomicLong(1); - transformations = new LinkedHashMap, Transformation>(); + transformations = new ConfigImpl() { + @Override + public Transformation wrap(Id aId, + Transformation aType) { + return new RobustTransformation(aId, aType); + } + }; vertices = new ArrayList(); matrix = new TransformationPath[0][0]; } + public Config getTransformationConfig() { + return new Config() { + @Override + public Id add(Transformation aT) { + return addTransformation(aT); + } + + @Override + public Transformation get(Id aId) { + return transformations.get(aId); + } + + @Override + public Collection> ids() { + return transformations.ids(); + } + + @Override + public boolean remove(Id 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 addTransformation(Transformation aTransformation) { - long seqno = sequenceNumber.getAndIncrement(); - Id id = new Id(seqno); - transformations.put(id, new RobustTransformation(id, aTransformation)); + Id 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 getPossibleTargetTypes(String aType) { int index = vertices.indexOf(aType); Set res = new HashSet(); @@ -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(); // Obtain possible starting points. Set v = new HashSet(); - for (Transformation transformation : transformations.values()) { + for (Id 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 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 aId) { transformations.remove(aId); computeTransformationSequences(); } - public Collection> getTransformations() { - return Collections.unmodifiableCollection(transformations.keySet()); - } - - public Transformation getTransformation(Id aId) { - return transformations.get(aId); - } - @Override public String toString() { StringBuffer buf = new StringBuffer(); diff --git a/impl/src/main/java/org/wamblee/xmlrouter/impl/XMLRouter.java b/impl/src/main/java/org/wamblee/xmlrouter/impl/XMLRouter.java index e86593d..99b3635 100644 --- a/impl/src/main/java/org/wamblee/xmlrouter/impl/XMLRouter.java +++ b/impl/src/main/java/org/wamblee/xmlrouter/impl/XMLRouter.java @@ -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, DocumentType> documentTypes; + + private Config documentTypes; private Transformations transformations; - private Map, Filter> filters; + private Config filters; private Map, 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, DocumentType>(); + documentTypes = new ConfigImpl() { + @Override + public DocumentType wrap(Id aId, DocumentType aType) { + return new RobustDocumentType(aId, aType); + } + }; transformations = new Transformations(); - filters = new LinkedHashMap, Filter>(); + filters = new ConfigImpl() { + @Override + public Filter wrap(Id aId, Filter aFilter) { + return new RobustFilter(aId, aFilter); + } + }; destinations = new LinkedHashMap, Destination>(); } @Override - public Id addDocumentType(DocumentType aType) { - long seqno = sequenceNumbers.getAndIncrement(); - documentTypes.put(new Id(seqno), aType); - return new Id(seqno); - } - - @Override - public void removeDocumentType(Id aId) { - documentTypes.remove(aId); - } - - @Override - public Collection> getDocumentTypes() { - return Collections.unmodifiableCollection(documentTypes.keySet()); - } - - @Override - public DocumentType getDocumentType(Id aId) { - return documentTypes.get(aId); - } - - @Override - public Id addTransformation(Transformation aTransformation) { - return transformations.addTransformation(aTransformation); - } - - @Override - public void removeTransformation(Id aId) { - transformations.removeTransformation(aId); - } - - @Override - public Collection> getTransformations() { - return transformations.getTransformations(); - } - - @Override - public Transformation getTransformation(Id aId) { - return transformations.getTransformation(aId); - } - - @Override - public Id addFilter(Filter aFilter) { - long seqno = sequenceNumbers.getAndIncrement(); - filters.put(new Id(seqno), aFilter); - return new Id(seqno); - } - - @Override - public void removeFilter(Id aId) { - filters.remove(aId); + public Config getDocumentTypeConfig() { + return documentTypes; } @Override - public Collection> getFilters() { - return Collections.unmodifiableCollection(filters.keySet()); + public Config getTransformationConfig() { + return transformations.getTransformationConfig(); } @Override - public Filter getFilter(Id aId) { - return filters.get(aId); + public Config 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 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 determineDocumentTypes(DOMSource aEvent) { List res = new ArrayList(); - for (DocumentType type : documentTypes.values()) { + for (Id 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 index 0000000..57289dc --- /dev/null +++ b/impl/src/test/java/org/wamblee/xmlrouter/impl/RobustDocumentTypeTest.java @@ -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(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()); + } +} diff --git a/impl/src/test/java/org/wamblee/xmlrouter/impl/XMLRouterTest.java b/impl/src/test/java/org/wamblee/xmlrouter/impl/XMLRouterTest.java index 47eb21a..9ee2645 100644 --- a/impl/src/test/java/org/wamblee/xmlrouter/impl/XMLRouterTest.java +++ b/impl/src/test/java/org/wamblee/xmlrouter/impl/XMLRouterTest.java @@ -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 typeId = router.addDocumentType(type); + Id 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 typeId = router.addDocumentType(type); + Id 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) 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) 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), -- 2.31.1