2 * Copyright 2005-2011 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.wamblee.xmlrouter.impl;
18 import static org.mockito.Matchers.*;
19 import static org.mockito.Mockito.*;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.UUID;
24 import java.util.logging.Handler;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
28 import javax.xml.transform.dom.DOMSource;
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.wamblee.general.SystemClock;
33 import org.wamblee.xmlrouter.common.Id;
34 import org.wamblee.xmlrouter.config.DocumentType;
35 import org.wamblee.xmlrouter.config.Filter;
36 import org.wamblee.xmlrouter.config.RouterConfig;
37 import org.wamblee.xmlrouter.config.Transformation;
38 import org.wamblee.xmlrouter.listener.EventInfo;
39 import org.wamblee.xmlrouter.listener.EventListener;
40 import org.wamblee.xmlrouter.listener.LoggingEventListener;
41 import org.wamblee.xmlrouter.subscribe.Destination;
43 public class XMLRouterTest {
45 public static class MyDestination implements Destination {
47 private boolean receiveResult;
48 private Collection<String> types;
50 public MyDestination(boolean aReceiveResult, Collection<String> aTypes) {
51 receiveResult = aReceiveResult;
56 public Collection<String> chooseFromTargetTypes(
57 Collection<String> aPossibleTargetTypes) {
62 public String getName() {
67 public boolean receive(DOMSource aEvent) {
72 private ExtendedRouterConfig routerConfig;
73 private XMLRouterConfiguration config;
74 private XMLRouter router;
75 private DOMSource source1;
76 private DOMSource source2;
77 private DOMSource source3;
79 private Destination destinationSpy;
80 private Id<Destination> destinationId;
81 private EventListener listener;
86 Logger logger = Logger.getLogger(XMLRouter.class.getName());
87 setLogLevel(logger, Level.FINEST);
89 routerConfig = new SingleRouterConfig(new Id<RouterConfig>(
91 config = new XMLRouterConfigurationImpl(routerConfig);
92 EventListener logListener = new LoggingEventListener(Level.INFO);
93 listener = spy(logListener);
94 router = new XMLRouter(new SystemClock(), config, listener);
95 source1 = mock(DOMSource.class);
96 source2 = mock(DOMSource.class);
97 source3 = mock(DOMSource.class);
100 private void setLogLevel(Logger aLogger, Level aLevel) {
101 aLogger.setLevel(aLevel);
102 for (Handler handler : aLogger.getHandlers()) {
103 handler.setLevel(Level.FINEST);
105 Logger parent = aLogger.getParent();
106 if (parent != null) {
107 setLogLevel(parent, aLevel);
112 public void testNoInputDocumentsRegistered() {
113 Destination destination = new MyDestination(true, Arrays.asList("any"));
114 destinationSpy = spy(destination);
116 destinationId = router.registerDestination(destinationSpy);
117 router.publish("any", source1);
118 verify(listener).notDelivered(any(EventInfo.class));
122 public void testMisBehavingDocumentType() {
123 DocumentType type = mockDocument("docid");
124 doThrow(new RuntimeException("x")).when(type).isInstance(
125 any(DOMSource.class));
126 routerConfig.documentTypeConfig().add(type);
127 router.publish("xx", mock(DOMSource.class));
128 verify(listener).notDelivered(any(EventInfo.class));
129 // no exception should occur.
132 private DocumentType mockDocument(String docid) {
133 DocumentType type = mock(DocumentType.class);
134 when(type.getId()).thenReturn(new Id<DocumentType>(docid));
139 public void testMisBehavingFilter() {
140 registerDocumentType("any");
141 Filter filter = mockFilter("filterid");
142 doThrow(new RuntimeException("x")).when(filter).isAllowed(anyString(),
143 any(DOMSource.class));
144 routerConfig.filterConfig().add(filter);
145 router.publish("xx", mock(DOMSource.class));
146 verify(listener).notDelivered(any(EventInfo.class));
147 // no exception should occur.
150 private Filter mockFilter(String filterId) {
151 Filter filter = mock(Filter.class);
152 when(filter.getId()).thenReturn(new Id<Filter>(filterId));
157 public void testOneDestinationNoTransformationSuccess() {
158 destinationSpy = registerDestination(true, "any");
159 registerDocumentType("any");
161 router.publish("any", source1);
162 verify(listener).delivered(any(EventInfo.class),
163 anyListOf(Transformation.class), anyString(), eq(true));
164 verify(destinationSpy).receive(same(source1));
166 // Unregister the destination.
167 router.unregisterDestination(destinationId);
169 router.publish("any", source2);
170 verify(listener).notDelivered(any(EventInfo.class));
171 verifyNoMoreInteractions(destinationSpy);
174 private void resetMocks() {
175 reset(destinationSpy);
179 private void registerDocumentType(String aType) {
180 DocumentType type = mockDocument(UUID.randomUUID().toString());
181 when(type.isInstance(any(DOMSource.class))).thenReturn(true);
182 when(type.getName()).thenReturn(aType);
183 routerConfig.documentTypeConfig().add(type);
186 private void registerDocumentType(String aType, DOMSource aSource) {
187 DocumentType type = mock(DocumentType.class);
188 when(type.isInstance(same(aSource))).thenReturn(true);
189 when(type.getName()).thenReturn(aType);
190 when(type.getId()).thenReturn(new Id<DocumentType>(aType));
191 routerConfig.documentTypeConfig().add(type);
194 private Destination registerDestination(boolean aResult, String... types) {
195 Destination destination = new MyDestination(aResult,
196 Arrays.asList(types));
197 Destination myspy = spy(destination);
198 destinationId = router.registerDestination(myspy);
203 public void testOneDestinationNotMatches() {
204 destinationSpy = registerDestination(true);
205 registerDocumentType("any");
207 router.publish("any", source1);
208 verify(listener).notDelivered(any(EventInfo.class));
209 verify(destinationSpy, never()).receive(any(DOMSource.class));
213 public void testOneDestinationThrowsException() {
214 destinationSpy = registerDestination(true, "any");
215 registerDocumentType("any");
217 doThrow(new RuntimeException()).when(destinationSpy).receive(
218 any(DOMSource.class));
220 router.publish("any", source1);
221 verify(listener).notDelivered(any(EventInfo.class));
222 verify(destinationSpy).receive(same(source1));
226 public void testOneDestinationThrowsExceptionSecondDestinationStillHandled() {
227 testOneDestinationThrowsException();
228 Destination destination2 = new MyDestination(true, Arrays.asList("any"));
229 Destination destinationSpy2 = spy(destination2);
230 Id<Destination> destinationId2 = router
231 .registerDestination(destinationSpy2);
233 router.publish("any", source1);
234 verify(listener).delivered(any(EventInfo.class),
235 anyListOf(Transformation.class), anyString(), eq(true));
237 verify(destinationSpy2).receive(same(source1));
242 public void testDestinationChooseFromTargetTypesThrowsException() {
243 destinationSpy = registerDestination(true, "any");
244 registerDocumentType("any");
246 doThrow(new RuntimeException()).when(destinationSpy)
247 .chooseFromTargetTypes((Collection<String>) anyObject());
249 router.publish("any", source1);
250 verify(listener).notDelivered(any(EventInfo.class));
251 verify(destinationSpy, never()).receive(same(source1));
255 public void testDestinationChooseFromTargetTypesThrowsExceptionSecondDestinationStillOk() {
256 testDestinationChooseFromTargetTypesThrowsException();
258 Destination destination2 = new MyDestination(true, Arrays.asList("any"));
259 Destination destinationSpy2 = spy(destination2);
260 Id<Destination> destinationId2 = router
261 .registerDestination(destinationSpy2);
262 router.publish("any", source1);
263 verify(listener).delivered(any(EventInfo.class),
264 anyListOf(Transformation.class), anyString(), eq(true));
266 verify(destinationSpy, never()).receive(same(source1));
267 verify(destinationSpy2).receive(same(source1));
271 public void testDestinationChooseFromTargetTypesReturnsNull() {
272 destinationSpy = registerDestination(true, "any");
273 registerDocumentType("any");
277 .chooseFromTargetTypes((Collection<String>) anyObject()))
280 router.publish("any", source1);
281 verify(listener).notDelivered(any(EventInfo.class));
282 verify(destinationSpy, never()).receive(same(source1));
286 public void testDestinationChooseFromTargetTypesReturnsNullSecondDestinationStillOk() {
287 testDestinationChooseFromTargetTypesReturnsNull();
289 Destination destination2 = new MyDestination(true, Arrays.asList("any"));
290 Destination destinationSpy2 = spy(destination2);
291 Id<Destination> destinationId2 = router
292 .registerDestination(destinationSpy2);
293 router.publish("any", source1);
294 verify(listener).delivered(any(EventInfo.class),
295 anyListOf(Transformation.class), anyString(), eq(true));
297 verify(destinationSpy, never()).receive(same(source1));
298 verify(destinationSpy2).receive(same(source1));
302 public void testOneTransformationOneDestination() {
303 registerDocumentType("any");
304 Transformation transformation = mock(Transformation.class);
305 when(transformation.getId())
306 .thenReturn(new Id<Transformation>("trans"));
307 when(transformation.getFromType()).thenReturn("any");
308 when(transformation.getToType()).thenReturn("bla");
309 when(transformation.transform(same(source1))).thenReturn(source2);
310 routerConfig.transformationConfig().add(transformation);
311 config.setRouterConfig(routerConfig);
313 Destination destination = mock(Destination.class);
315 destination.chooseFromTargetTypes((Collection<String>) anyObject()))
316 .thenReturn(Arrays.asList("bla"));
318 router.registerDestination(destination);
320 when(destination.receive(any(DOMSource.class))).thenReturn(true);
321 router.publish("bla", source1);
322 verify(listener).delivered(any(EventInfo.class),
323 anyListOf(Transformation.class), anyString(), eq(true));
325 verify(transformation).transform(source1);
326 verify(destination).receive(same(source2));
328 // now the same when the destination rejects the event.
329 when(destination.receive(any(DOMSource.class))).thenReturn(false);
330 router.publish("bla", source1);
331 verify(listener).notDelivered(any(EventInfo.class));
335 public void testOneTransformationOneDestinationFilterRejectsDestinationDocType() {
336 registerDocumentType("any");
338 Transformation transformation = mock(Transformation.class);
339 when(transformation.getId())
340 .thenReturn(new Id<Transformation>("trans"));
341 when(transformation.getFromType()).thenReturn("any");
342 when(transformation.getToType()).thenReturn("bla");
343 when(transformation.transform(same(source1))).thenReturn(source2);
344 routerConfig.transformationConfig().add(transformation);
346 Filter filter = mock(Filter.class);
347 when(filter.getId()).thenReturn(new Id<Filter>("f"));
348 when(filter.isAllowed(anyString(), same(source2))).thenReturn(false);
349 when(filter.isAllowed(anyString(), same(source1))).thenReturn(true);
350 routerConfig.filterConfig().add(filter);
352 config.setRouterConfig(routerConfig);
354 Destination destination = mock(Destination.class);
356 destination.chooseFromTargetTypes((Collection<String>) anyObject()))
357 .thenReturn(Arrays.asList("bla"));
359 router.registerDestination(destination);
361 when(destination.receive(any(DOMSource.class))).thenReturn(true);
362 router.publish("bla", source1);
363 verify(listener).notDelivered(any(EventInfo.class));
365 verify(transformation).transform(source1);
366 verify(destination, never()).receive(any(DOMSource.class));
370 public void testMisbehavingTransformationOneDestination() {
371 registerDocumentType("any");
372 Transformation transformation = mock(Transformation.class);
373 when(transformation.getId())
374 .thenReturn(new Id<Transformation>("trans"));
375 when(transformation.getFromType()).thenReturn("any");
376 when(transformation.getToType()).thenReturn("bla");
377 doThrow(new RuntimeException("x")).when(transformation).transform(
379 routerConfig.transformationConfig().add(transformation);
381 Destination destination = mock(Destination.class);
383 destination.chooseFromTargetTypes((Collection<String>) anyObject()))
384 .thenReturn(Arrays.asList("bla"));
386 router.registerDestination(destination);
388 when(destination.receive(any(DOMSource.class))).thenReturn(true);
389 router.publish("bla", source1);
390 verify(listener).notDelivered(any(EventInfo.class));
393 private Transformation createTransformation(String aFrom, String aTo,
394 DOMSource aSource, DOMSource aTarget) {
395 Transformation transformation = mock(Transformation.class);
396 when(transformation.getId()).thenReturn(
397 new Id<Transformation>(UUID.randomUUID().toString()));
398 when(transformation.getFromType()).thenReturn(aFrom);
399 when(transformation.getToType()).thenReturn(aTo);
400 when(transformation.transform(same(aSource))).thenReturn(aTarget);
401 return transformation;
405 public void testOneTransformationReturnsNull() {
406 registerDocumentType("any");
407 Transformation transformation = createTransformation("any", "bla",
410 routerConfig.transformationConfig().add(transformation);
411 config.setRouterConfig(routerConfig);
412 Destination destination = mock(Destination.class);
414 destination.chooseFromTargetTypes((Collection<String>) anyObject()))
415 .thenReturn(Arrays.asList("bla"));
416 router.registerDestination(destination);
418 router.publish("bla", source1);
419 verify(listener).notDelivered(any(EventInfo.class));
421 verify(transformation).transform(source1);
422 verify(destination, never()).receive(any(DOMSource.class));
424 // add second transformation that behaves normally
425 Transformation transformation2 = createTransformation("any", "bla2",
428 routerConfig.transformationConfig().add(transformation2);
429 config.setRouterConfig(routerConfig);
431 destination.chooseFromTargetTypes((Collection<String>) anyObject()))
432 .thenReturn(Arrays.asList("bla", "bla2"));
434 reset(transformation);
435 when(transformation.getId())
436 .thenReturn(new Id<Transformation>("trans"));
437 when(transformation.getFromType()).thenReturn("any");
438 when(transformation.getToType()).thenReturn("bla");
439 when(transformation.transform(same(source1))).thenReturn(null);
441 when(destination.receive(any(DOMSource.class))).thenReturn(true);
442 router.publish("bla", source1);
443 verify(listener).delivered(any(EventInfo.class),
444 anyListOf(Transformation.class), anyString(), eq(true));
446 verify(transformation).transform(source1);
447 verify(transformation2).transform(source1);
449 verify(destination).receive(same(source2));
454 public void testChooseMultipleDestinationsOneType() {
455 Destination dest1 = registerDestination(true, "any");
456 Destination dest2 = registerDestination(true, "any");
457 registerDocumentType("any");
459 router.publish("source", source1);
460 verify(listener, times(2)).delivered(any(EventInfo.class),
461 anyListOf(Transformation.class), anyString(), eq(true));
463 verify(dest1).receive(same(source1));
464 verify(dest2).receive(same(source1));
468 public void testMultipleDeliveryToOneDestination() {
469 Destination dest = registerDestination(true, "any", "other");
470 registerDocumentType("any", source1);
471 registerDocumentType("other", source2);
472 Transformation transformation = createTransformation("any", "other",
474 routerConfig.transformationConfig().add(transformation);
475 config.setRouterConfig(routerConfig);
477 router.publish("source", source1);
478 verify(listener, times(2)).delivered(any(EventInfo.class),
479 anyListOf(Transformation.class), anyString(), eq(true));
481 verify(dest).receive(same(source1));
482 verify(dest).receive(same(source2));
486 public void testMultipleTransformations() {
487 Destination dest = registerDestination(true, "other");
488 registerDocumentType("any", source1);
489 registerDocumentType("other", source3);
491 Transformation t1 = createTransformation("any", "intermediate",
493 routerConfig.transformationConfig().add(t1);
494 Transformation t2 = createTransformation("intermediate", "other",
496 routerConfig.transformationConfig().add(t2);
497 config.setRouterConfig(routerConfig);
499 router.publish("source", source1);
500 verify(listener).delivered(any(EventInfo.class),
501 anyListOf(Transformation.class), anyString(), eq(true));
503 verify(dest).receive(same(source3));
507 public void testMultipleTransformationsFilterRejectsIntermediateDocument() {
508 Destination dest = registerDestination(true, "other");
509 registerDocumentType("any", source1);
510 registerDocumentType("other", source3);
512 Transformation t1 = createTransformation("any", "intermediate",
514 routerConfig.transformationConfig().add(t1);
515 Transformation t2 = createTransformation("intermediate", "other",
517 routerConfig.transformationConfig().add(t2);
518 config.setRouterConfig(routerConfig);
520 Filter filter = mock(Filter.class);
521 when(filter.getId()).thenReturn(new Id<Filter>("f"));
522 when(filter.isAllowed(anyString(), same(source1))).thenReturn(true);
523 when(filter.isAllowed(anyString(), same(source2))).thenReturn(false);
524 when(filter.isAllowed(anyString(), same(source3))).thenReturn(true);
525 routerConfig.filterConfig().add(filter);
527 router.publish("source", source1);
528 verify(listener).notDelivered(any(EventInfo.class));
529 verify(dest, never()).receive(any(DOMSource.class));
533 public void testDestinationGivesError() {
534 Destination destination = mock(Destination.class);
535 when(destination.getName()).thenReturn("name");
536 when(destination.chooseFromTargetTypes(anyCollectionOf(String.class)))
537 .thenReturn(Arrays.asList("any"));
538 doThrow(new RuntimeException("x")).when(destination).receive(
539 any(DOMSource.class));
540 router.registerDestination(destination);
542 registerDocumentType("any");
544 router.publish("source", source1);
546 verify(listener).delivered(any(EventInfo.class),
547 anyListOf(Transformation.class), anyString(), eq(false));