1 package org.wamblee.xmlrouter.impl;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.HashSet;
7 import java.util.LinkedHashMap;
11 import java.util.concurrent.atomic.AtomicInteger;
12 import java.util.logging.Level;
13 import java.util.logging.Logger;
15 import javax.xml.transform.dom.DOMSource;
17 import org.wamblee.xml.XMLDocument;
18 import org.wamblee.xmlrouter.common.Id;
19 import org.wamblee.xmlrouter.config.Config;
20 import org.wamblee.xmlrouter.config.DocumentType;
21 import org.wamblee.xmlrouter.config.Filter;
22 import org.wamblee.xmlrouter.config.Transformation;
23 import org.wamblee.xmlrouter.publish.Gateway;
24 import org.wamblee.xmlrouter.subscribe.Destination;
25 import org.wamblee.xmlrouter.subscribe.DestinationRegistry;
29 public class XMLRouter implements Config, Gateway, DestinationRegistry {
31 private static final Logger LOGGER = Logger.getLogger(XMLRouter.class
34 private AtomicInteger sequenceNumbers;
35 private Map<Integer, DocumentType> documentTypes;
36 private Transformations transformations;
37 private Map<Integer, Filter> filters;
38 private Map<Integer, Destination> destinations;
41 sequenceNumbers = new AtomicInteger(1);
42 documentTypes = new LinkedHashMap<Integer, DocumentType>();
43 transformations = new Transformations();
44 filters = new LinkedHashMap<Integer, Filter>();
45 destinations = new LinkedHashMap<Integer, Destination>();
49 public Id<DocumentType> addDocumentType(DocumentType aType) {
50 int seqno = sequenceNumbers.getAndIncrement();
51 documentTypes.put(seqno, aType);
52 return new Id<DocumentType>(seqno);
56 public void removeDocumentType(Id<DocumentType> aId) {
57 documentTypes.remove(aId);
61 public Collection<DocumentType> getDocumentTypes() {
62 return Collections.unmodifiableCollection(documentTypes.values());
66 public Id<Transformation> addTransformation(Transformation aTransformation) {
67 return transformations.addTransformation(aTransformation);
71 public void removeTransformation(Id<Transformation> aId) {
72 transformations.removeTransformation(aId);
76 public Collection<Transformation> getTransformations() {
77 return transformations.getTransformations();
81 public Id<Filter> addFilter(Filter aFilter) {
82 int seqno = sequenceNumbers.getAndIncrement();
83 filters.put(seqno, aFilter);
84 return new Id<Filter>(seqno);
88 public void removeFilter(Id<Filter> aId) {
93 public Collection<Filter> getFilters() {
94 return Collections.unmodifiableCollection(filters.values());
98 public boolean publish(String aSource, DOMSource aEvent) {
100 boolean delivered = false;
103 List<String> filteredInputTypes = determineFilteredInputTypes(aEvent);
104 if (filteredInputTypes.isEmpty()) {
105 if (LOGGER.isLoggable(Level.FINE)) {
106 String doc = new XMLDocument(aEvent).print(true);
110 "Event ''0}'' from source {1} removed because of filters.",
111 new Object[] { doc, aSource });
116 // get the reachable target types through transformations.
118 // It is possible that a given event belongs to multiple input
120 // This is however certainly not the main case.
122 for (String inputType : filteredInputTypes) {
123 boolean result = deliverEvent(aSource, aEvent, inputType);
124 delivered = delivered || result;
128 destinationNotFound(aSource, aEvent);
134 private boolean deliverEvent(String aSource, DOMSource aEvent,
137 boolean delivered = false;
138 Set<String> possibleTargetTypes = new HashSet<String>();
139 possibleTargetTypes.addAll(transformations
140 .getPossibleTargetTypes(aInputType));
142 // ask each destination what target types, if any they want to have.
143 for (Destination destination : destinations.values()) {
144 Collection<String> requested = destination
145 .chooseFromTargetTypes(possibleTargetTypes);
146 if (!requested.isEmpty()) {
147 // Deliver to the destination.
148 for (String targetType : requested) {
149 TransformationPath path = transformations.getPath(
150 aInputType, targetType);
151 List<Transformation> ts = path.getTransformations();
153 boolean allowed = true;
154 DOMSource transformed = aEvent;
155 while (i < ts.size() && allowed && transformed != null) {
156 Transformation t = ts.get(i);
157 DOMSource orig = transformed;
158 transformed = t.transform(transformed);
159 if (transformed == null) {
160 transformationReturnedNull(aSource, aEvent,
161 aInputType, t, orig);
164 if (!isAllowedByFilters(t.getToType(), transformed)) {
169 if (allowed && transformed != null) {
170 // all transformations done and all filters still
172 boolean result = destination.receive(transformed);
173 delivered = delivered || result;
182 private List<String> determineFilteredInputTypes(DOMSource aEvent) {
183 List<String> types = determineDocumentTypes(aEvent);
184 // apply filters to the input
185 List<String> filteredTypes = new ArrayList<String>();
186 for (String type : types) {
187 boolean allowed = isAllowedByFilters(type, aEvent);
189 filteredTypes.add(type);
192 return filteredTypes;
195 private boolean isAllowedByFilters(String aType, DOMSource aEvent) {
196 boolean allowed = true;
197 for (Filter filter : filters.values()) {
198 if (!filter.isAllowed(aType, aEvent)) {
205 private List<String> determineDocumentTypes(DOMSource aEvent) {
206 List<String> res = new ArrayList<String>();
207 for (DocumentType type : documentTypes.values()) {
208 if (type.isInstance(aEvent)) {
209 res.add(type.getName());
215 private void logEvent(String aMessage, String aSource, DOMSource aEvent,
216 Exception aException) {
217 LOGGER.log(Level.WARNING, aMessage + ": source '" + aSource +
218 "': Event: '" + new XMLDocument(aEvent).print(true) + "'",
222 private void logEvent(String aMessage, String aSource, DOMSource aEvent) {
223 LOGGER.log(Level.WARNING,
224 aMessage + ": " + eventToString(aSource, aEvent));
227 private String eventToString(String aSource, DOMSource aEvent) {
228 return "source '" + aSource + "': Event: '" +
229 new XMLDocument(aEvent).print(true) + "'";
232 private void transformationReturnedNull(String aSource, DOMSource aEvent,
233 String aInputType, Transformation aT, DOMSource aTransformed) {
234 LOGGER.log(Level.WARNING, "Transformation returned null for event " +
235 eventToString(aSource, aEvent) + " inputType '" + aInputType +
236 "', transformation '" + aT + "' document to transform " +
237 new XMLDocument(aTransformed).print(true));
240 private void destinationNotFound(String aSource, DOMSource aEvent) {
241 LOGGER.log(Level.WARNING, "No destination found for event: " +
242 eventToString(aSource, aEvent));
246 public Id<Destination> registerDestination(Destination aDestination) {
247 notNull("destination", aDestination);
248 int seqno = sequenceNumbers.getAndIncrement();
249 Id<Destination> id = new Id<Destination>(seqno);
250 destinations.put(seqno, new RobustDestination(id, aDestination));
255 public void unregisterDestination(Id<Destination> aId) {
256 destinations.remove(aId.getId());
259 private void notNull(String aName, Object aValue) {
260 if (aValue == null) {
261 throw new IllegalArgumentException("Parameter '" + aName +
262 "' may not be null");