*/
package org.wamblee.xmlrouter.impl;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
import org.wamblee.xmlrouter.common.Id;
-import org.wamblee.xmlrouter.config.RouterConfig;
-import org.wamblee.xmlrouter.config.RouterConfigService;
+import org.wamblee.xmlrouter.config.Config;
-/**
- * Router configuration service providing an atomic configuration API for the
- * XML router.
- *
- * @author Erik Brakkee
- */
-public class RouterConfigServiceImpl implements RouterConfigService {
+public class CompositeConfig<T> implements ExtendedConfig<T> {
- @Override
- public RouterConfig emptyConfig() {
- // TODO Auto-generated method stub
- return null;
+ private Map<Id<T>, T> configs;
+
+ public CompositeConfig() {
+ configs = new LinkedHashMap<Id<T>, T>();
+ }
+
+ public void add(Config<T> aConfig) {
+ for (Id<T> id : aConfig.map().keySet()) {
+ configs.put(id, aConfig.map().get(id));
+ }
}
@Override
- public Id<RouterConfig> apply(RouterConfig aConfig,
- Id<RouterConfig> aOldConfig) {
- // TODO Auto-generated method stub
- return null;
+ public Map<Id<T>, T> map() {
+ return configs;
}
@Override
- public void clear(Id<RouterConfig> aConfig) {
- // TODO Auto-generated method stub
+ public boolean remove(Id<T> aId) {
+ notSupported();
+ return false;
+ }
+ private void notSupported() {
+ throw new RuntimeException("readonly instance");
}
+ @Override
+ public Id<T> add(T aT) {
+ notSupported();
+ return null;
+ }
}
--- /dev/null
+/*
+ * 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 org.wamblee.xmlrouter.config.Config;
+import org.wamblee.xmlrouter.config.DocumentType;
+import org.wamblee.xmlrouter.config.Filter;
+import org.wamblee.xmlrouter.config.RouterConfig;
+import org.wamblee.xmlrouter.config.Transformation;
+
+public class CompositeRouterConfig implements ExtendedRouterConfig {
+
+ private CompositeConfig<DocumentType> documentTypes;
+ private CompositeConfig<Transformation> transformations;
+ private CompositeConfig<Filter> filters;
+
+ public CompositeRouterConfig(Collection<RouterConfig> aConfigs) {
+ documentTypes = new CompositeConfig<DocumentType>();
+ transformations = new CompositeConfig<Transformation>();
+ filters = new CompositeConfig<Filter>();
+ for (RouterConfig config : aConfigs) {
+ documentTypes.add(config.documentTypeConfig());
+ transformations.add(config.transformationConfig());
+ filters.add(config.filterConfig());
+ }
+ }
+
+ @Override
+ public Config<DocumentType> documentTypeConfig() {
+ return documentTypes;
+ }
+
+ @Override
+ public Config<Transformation> transformationConfig() {
+ return transformations;
+ }
+
+ @Override
+ public Config<Filter> filterConfig() {
+ return filters;
+ }
+}
throw new NullPointerException("Id is null");
}
}
-
- @Override
- public boolean isDirty() {
- return dirty;
- }
-
- @Override
- public void resetDirty() {
- dirty = false;
- }
}
public interface ExtendedConfig<T> extends Config<T> {
- boolean isDirty();
-
- void resetDirty();
}
* Locks the configuration object from further changes.
*/
// void lock();
-
- /**
- * Checks whether or not the configuration has changed.
- *
- * @return True iff dirty.
- */
- boolean isDirty();
-
- /**
- * Resets the router configuration dirty state.
- */
- void resetDirty();
}
public Config<Filter> filterConfig() {
return filters;
}
-
- @Override
- public boolean isDirty() {
- return documentTypes.isDirty() || transformations.isDirty() ||
- filters.isDirty();
- }
-
- @Override
- public void resetDirty() {
- documentTypes.resetDirty();
- filters.resetDirty();
- transformations.resetDirty();
- }
-
}
private void publishImpl(String aSource, DOMSource aEvent) {
long time = clock.currentTimeMillis();
- // TODO dirty flag will become unnecessary in the future.
- if (config.routerConfig().isDirty()) {
- config.transformations().replaceTransformations(
- config.routerConfig().transformationConfig().map());
- config.routerConfig().resetDirty();
- }
-
Id<DOMSource> id = new Id<DOMSource>(nextEventId.getAndIncrement());
List<String> types = determineDocumentTypes(aEvent);
EventInfo info = new EventInfo(time, aSource, id, types, aEvent);
boolean delivered = false;
Set<String> possibleTargetTypes = new HashSet<String>();
- possibleTargetTypes.addAll(config.transformations()
+ possibleTargetTypes.addAll(config.getTransformations()
.getPossibleTargetTypes(aInputType));
// ask each destination what target types, if any they want to have.
if (!requested.isEmpty()) {
// Deliver to the destination.
for (String targetType : requested) {
- TransformationPath path = config.transformations().getPath(
- aInputType, targetType);
+ TransformationPath path = config.getTransformations()
+ .getPath(aInputType, targetType);
List<Transformation> ts = path.getTransformations();
int i = 0;
boolean allowed = true;
private boolean isAllowedByFilters(String aType, DOMSource aEvent) {
boolean allowed = true;
- for (Filter filter : config.routerConfig().filterConfig().map()
+ for (Filter filter : config.getRouterConfig().filterConfig().map()
.values()) {
if (!filter.isAllowed(aType, aEvent)) {
allowed = false;
private List<String> determineDocumentTypes(DOMSource aEvent) {
List<String> res = new ArrayList<String>();
- for (DocumentType type : config.routerConfig().documentTypeConfig()
+ for (DocumentType type : config.getRouterConfig().documentTypeConfig()
.map().values()) {
if (type.isInstance(aEvent)) {
res.add(type.getName());
--- /dev/null
+/*
+ * 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.concurrent.atomic.AtomicLong;
+
+import org.wamblee.xmlrouter.common.Id;
+import org.wamblee.xmlrouter.config.Config;
+import org.wamblee.xmlrouter.config.RouterConfig;
+import org.wamblee.xmlrouter.config.RouterConfigService;
+
+/**
+ * Router configuration service providing an atomic configuration API for the
+ * XML router.
+ *
+ * @author Erik Brakkee
+ */
+public class XMLRouterConfigService implements RouterConfigService {
+
+ private AtomicLong sequence;
+ private XMLRouterConfiguration config;
+ private Config<RouterConfig> routerConfigs;
+
+ public XMLRouterConfigService(XMLRouterConfiguration aConfig) {
+ sequence = new AtomicLong(1L);
+ config = aConfig;
+ routerConfigs = new ConfigImpl<RouterConfig>(sequence) {
+ public RouterConfig wrap(Id<RouterConfig> aId, RouterConfig aT) {
+ return aT;
+ }
+ };
+ }
+
+ @Override
+ public RouterConfig emptyConfig() {
+ return new SingleRouterConfig(sequence);
+ }
+
+ @Override
+ public Id<RouterConfig> apply(RouterConfig aConfig,
+ Id<RouterConfig> aOldConfig) {
+ config.startConfigurationChange();
+ try {
+ return applyImpl(aConfig, aOldConfig);
+ } finally {
+ config.endConfigurationChange();
+ }
+ }
+
+ private Id<RouterConfig> applyImpl(RouterConfig aConfig,
+ Id<RouterConfig> aOldConfig) {
+ if (aOldConfig != null) {
+ routerConfigs.remove(aOldConfig);
+ }
+ Id<RouterConfig> id = routerConfigs.add(aConfig);
+ update();
+ return id;
+ }
+
+ @Override
+ public void clear(Id<RouterConfig> aConfig) {
+ config.startConfigurationChange();
+ try {
+ clearImpl(aConfig);
+ } finally {
+ config.endConfigurationChange();
+ }
+ }
+
+ private void clearImpl(Id<RouterConfig> aConfig) {
+ routerConfigs.remove(aConfig);
+ update();
+ }
+
+ private void update() {
+ ExtendedRouterConfig newconfig = new CompositeRouterConfig(
+ routerConfigs.map().values());
+ config.setRouterConfig(newconfig);
+ }
+
+}
/**
* @return Configuration data.
*/
- ExtendedRouterConfig routerConfig();
+ ExtendedRouterConfig getRouterConfig();
+
+ /**
+ * Sets the configuration and updates derived data.
+ *
+ * @param aConfig
+ * Configuration to set.
+ */
+ void setRouterConfig(ExtendedRouterConfig aConfig);
/**
* @return Transformation paths (derived data).
*/
- TransformationPaths transformations();
+ TransformationPaths getTransformations();
}
*/
package org.wamblee.xmlrouter.impl;
+import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.wamblee.xmlrouter.config.RouterConfig;
+
/**
* Implements the XML Router configuration interface including the required
* locking.
private ExtendedRouterConfig config;
private TransformationPaths transformations;
- public XMLRouterConfigurationImpl(ExtendedRouterConfig aConfig,
- TransformationPaths aTransformations) {
+ public XMLRouterConfigurationImpl(ExtendedRouterConfig aConfig) {
config = aConfig;
- transformations = aTransformations;
+ transformations = new TransformationPaths();
+ }
+
+ public XMLRouterConfigurationImpl() {
+ this(new CompositeRouterConfig(new ArrayList<RouterConfig>()));
}
@Override
}
@Override
- public ExtendedRouterConfig routerConfig() {
+ public ExtendedRouterConfig getRouterConfig() {
return config;
}
@Override
- public TransformationPaths transformations() {
+ public void setRouterConfig(ExtendedRouterConfig aConfig) {
+ config = aConfig;
+ transformations.replaceTransformations(config.transformationConfig()
+ .map());
+ }
+
+ @Override
+ public TransformationPaths getTransformations() {
return transformations;
}
}
@Test
public void testAdd() {
MyType type1 = mock(MyType.class);
- assertFalse(config.isDirty());
Id<MyType> id1 = config.add(type1);
assertEquals(1, config.map().size());
assertTrue(config.map().get(id1) instanceof MyTypeWrapper);
assertSame(type1, ((MyTypeWrapper) config.map().get(id1)).getType());
- assertTrue(config.isDirty());
-
- config.resetDirty();
- assertFalse(config.isDirty());
// add another one.
MyType type2 = mock(MyType.class);
assertNotNull(id2);
assertEquals(2, config.map().size());
assertFalse(id1.equals(id2));
- assertTrue(config.isDirty());
-
}
@Test
assertNotNull(id1);
assertEquals(1, config.map().size());
- config.resetDirty();
- assertFalse(config.isDirty());
-
config.remove(id1);
assertTrue(config.map().isEmpty());
- assertTrue(config.isDirty());
}
@Test(expected = UnsupportedOperationException.class)
DocumentType type1 = mock(DocumentType.class);
DocumentType type2 = mock(DocumentType.class);
- assertFalse(config.isDirty());
Id<DocumentType> id1 = config.documentTypeConfig().add(type1);
- assertTrue(config.isDirty());
- config.resetDirty();
- assertFalse(config.isDirty());
Id<DocumentType> id2 = config.documentTypeConfig().add(type2);
assertFalse(id1.equals(id2));
- assertTrue(config.isDirty());
assertEquals(2, config.documentTypeConfig().map().size());
assertTrue(config.documentTypeConfig().map().get(id1) instanceof RobustDocumentType);
Transformation transformation1 = mock(Transformation.class);
Transformation transformation2 = mock(Transformation.class);
- assertFalse(config.isDirty());
Id<Transformation> id1 = config.transformationConfig().add(
transformation1);
- assertTrue(config.isDirty());
- config.resetDirty();
- assertFalse(config.isDirty());
Id<Transformation> id2 = config.transformationConfig().add(
transformation2);
assertFalse(id1.equals(id2));
- assertTrue(config.isDirty());
assertEquals(2, config.transformationConfig().map().size());
assertTrue(config.transformationConfig().map().get(id1) instanceof RobustTransformation);
Filter filter1 = mock(Filter.class);
Filter filter2 = mock(Filter.class);
- assertFalse(config.isDirty());
Id<Filter> id1 = config.filterConfig().add(filter1);
- assertTrue(config.isDirty());
- config.resetDirty();
- assertFalse(config.isDirty());
Id<Filter> id2 = config.filterConfig().add(filter2);
assertFalse(id1.equals(id2));
- assertTrue(config.isDirty());
assertEquals(2, config.filterConfig().map().size());
assertTrue(config.filterConfig().map().get(id1) instanceof RobustFilter);
--- /dev/null
+/*
+ * 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 org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.logging.Level;
+
+import javax.xml.transform.dom.DOMSource;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.wamblee.general.SystemClock;
+import org.wamblee.xmlrouter.config.DocumentType;
+import org.wamblee.xmlrouter.config.RouterConfig;
+import org.wamblee.xmlrouter.config.Transformation;
+import org.wamblee.xmlrouter.listener.EventInfo;
+import org.wamblee.xmlrouter.listener.EventListener;
+import org.wamblee.xmlrouter.listener.LoggingEventListener;
+import org.wamblee.xmlrouter.subscribe.Destination;
+
+public class XMLRouterFunctionTest {
+
+ private XMLRouterConfiguration config;
+ private XMLRouter routerService;
+ private XMLRouterConfigService configService;
+
+ private EventListener listener;
+
+ private DOMSource source1;
+ private DOMSource source2;
+
+ @Before
+ public void setUp() {
+ config = new XMLRouterConfigurationImpl();
+ EventListener logListener = new LoggingEventListener(Level.INFO);
+ listener = spy(logListener);
+ routerService = new XMLRouter(new SystemClock(), config, listener);
+ configService = new XMLRouterConfigService(config);
+
+ source1 = mock(DOMSource.class);
+ source2 = mock(DOMSource.class);
+ }
+
+ private RouterConfig registerDocumentType(String aType) {
+ DocumentType type = mock(DocumentType.class);
+ when(type.isInstance(any(DOMSource.class))).thenReturn(true);
+ when(type.getName()).thenReturn(aType);
+ RouterConfig routerConfig = configService.emptyConfig();
+ routerConfig.documentTypeConfig().add(type);
+ return routerConfig;
+ }
+
+ @Test
+ public void testOneTransformationOneDestination() {
+ RouterConfig routerConfig = registerDocumentType("any");
+ Transformation transformation = mock(Transformation.class);
+ when(transformation.getName()).thenReturn("trans");
+ when(transformation.getFromType()).thenReturn("any");
+ when(transformation.getToType()).thenReturn("bla");
+ when(transformation.transform(same(source1))).thenReturn(source2);
+ routerConfig.transformationConfig().add(transformation);
+
+ configService.apply(routerConfig, null);
+
+ Destination destination = mock(Destination.class);
+ when(
+ destination.chooseFromTargetTypes((Collection<String>) anyObject()))
+ .thenReturn(Arrays.asList("bla"));
+
+ routerService.registerDestination(destination);
+
+ when(destination.receive(any(DOMSource.class))).thenReturn(true);
+ routerService.publish("bla", source1);
+ verify(listener).delivered(any(EventInfo.class),
+ anyListOf(Transformation.class), anyLong(), anyString(), eq(true));
+
+ verify(transformation).transform(source1);
+ verify(destination).receive(same(source2));
+
+ // now the same when the destination rejects the event.
+ when(destination.receive(any(DOMSource.class))).thenReturn(false);
+ routerService.publish("bla", source1);
+ verify(listener).notDelivered(any(EventInfo.class));
+ }
+}
@Before
public void setUp() {
routerConfig = new SingleRouterConfig(new AtomicLong(1L));
- config = new XMLRouterConfigurationImpl(routerConfig,
- new TransformationPaths());
+ config = new XMLRouterConfigurationImpl(routerConfig);
EventListener logListener = new LoggingEventListener(Level.INFO);
listener = spy(logListener);
router = new XMLRouter(new SystemClock(), config, listener);
when(transformation.getToType()).thenReturn("bla");
when(transformation.transform(same(source1))).thenReturn(source2);
routerConfig.transformationConfig().add(transformation);
+ config.getTransformations().replaceTransformations(
+ routerConfig.transformationConfig().map());
Destination destination = mock(Destination.class);
when(
source1, null);
routerConfig.transformationConfig().add(transformation);
+ config.getTransformations().replaceTransformations(
+ routerConfig.transformationConfig().map());
Destination destination = mock(Destination.class);
when(
source1, source2);
routerConfig.transformationConfig().add(transformation2);
+ config.getTransformations().replaceTransformations(
+ routerConfig.transformationConfig().map());
when(
destination.chooseFromTargetTypes((Collection<String>) anyObject()))
.thenReturn(Arrays.asList("bla", "bla2"));
Transformation transformation = createTransformation("any", "other",
source1, source2);
routerConfig.transformationConfig().add(transformation);
+ config.getTransformations().replaceTransformations(
+ routerConfig.transformationConfig().map());
router.publish("source", source1);
verify(listener, times(2)).delivered(any(EventInfo.class),
Transformation t2 = createTransformation("intermediate", "other",
source2, source3);
routerConfig.transformationConfig().add(t2);
+ config.getTransformations().replaceTransformations(
+ routerConfig.transformationConfig().map());
router.publish("source", source1);
verify(listener).delivered(any(EventInfo.class),