/* * Copyright 2015 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.springframework.xd.dirt.module; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertThat; import static org.springframework.xd.module.ModuleType.processor; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import org.hamcrest.Description; import org.hamcrest.DiagnosingMatcher; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.util.FileCopyUtils; import org.springframework.xd.module.ModuleDefinition; import org.springframework.xd.module.SimpleModuleDefinition; public class SynchronizingModuleRegistryTests { @Rule public TemporaryFolder sourceRoot = new TemporaryFolder(); @Rule public TemporaryFolder targetARoot = new TemporaryFolder(); @Rule public TemporaryFolder targetBRoot = new TemporaryFolder(); private WritableResourceModuleRegistry sourceRegistry1; // A "source" registry that will point to the same FS (simulates shared FS) private WritableResourceModuleRegistry sourceRegistry2; // Two target registries using different FS (simulates two different nodes) private WritableResourceModuleRegistry targetRegistry1; private WritableResourceModuleRegistry targetRegistry2; private SynchronizingModuleRegistry synch1; private SynchronizingModuleRegistry synch2; private ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); @Before public void setup() throws Exception { sourceRegistry1 = new WritableResourceModuleRegistry(sourceRoot.getRoot().toURI().toString()); sourceRegistry2 = new WritableResourceModuleRegistry(sourceRoot.getRoot().toURI().toString()); targetRegistry1 = new WritableResourceModuleRegistry(targetARoot.getRoot().toURI().toString()); targetRegistry2 = new WritableResourceModuleRegistry(targetBRoot.getRoot().toURI().toString()); sourceRegistry1.afterPropertiesSet(); sourceRegistry2.afterPropertiesSet(); targetRegistry1.afterPropertiesSet(); targetRegistry2.afterPropertiesSet(); synch1 = new SynchronizingModuleRegistry(sourceRegistry1, targetRegistry1); synch2 = new SynchronizingModuleRegistry(sourceRegistry2, targetRegistry2); } @Test public void testStaleBecauseOfDifferentContentsFindDefinition() { targetRegistry1.registerNew(new UploadedModuleDefinition("foo", processor, "hello".getBytes())); synch1.registerNew(new UploadedModuleDefinition("foo", processor, "world".getBytes())); ModuleDefinition result = synch1.findDefinition("foo", processor); assertThat(result, pointsToContentsThat(equalTo("world"))); } @Test public void testFindDefinitionWithInexistentTarget() { sourceRegistry1.registerNew(new UploadedModuleDefinition("foo", processor, "world".getBytes())); ModuleDefinition result = synch1.findDefinition("foo", processor); assertThat(result, pointsToContentsThat(equalTo("world"))); } @Test public void testNotFoundGoesThrough() { assertThat(synch1.findDefinition("idontexist", processor), is(nullValue())); } @Test public void registerOnOneIsVisibleOnTwo() { synch1.registerNew(new UploadedModuleDefinition("fizz", processor, "bonjour".getBytes())); ModuleDefinition result = synch2.findDefinition("fizz", processor); assertThat(result, pointsToContentsThat(equalTo("bonjour"))); } @Test public void deletionViaOneIsReflectedOnTwo_SingleFind() { UploadedModuleDefinition def = new UploadedModuleDefinition("fizz", processor, "bonjour".getBytes()); synch1.registerNew(def); ModuleDefinition result = synch2.findDefinition("fizz", processor); assertThat(result, pointsToContentsThat(equalTo("bonjour"))); synch1.delete(def); result = synch2.findDefinition("fizz", processor); assertThat(result, Matchers.nullValue()); } @Test public void deletionViaOneIsReflectedOnTwo_MultiFind() { UploadedModuleDefinition def = new UploadedModuleDefinition("fizz", processor, "bonjour".getBytes()); synch1.registerNew(def); List<ModuleDefinition> definitions = synch2.findDefinitions(processor); assertThat(definitions, contains(pointsToContentsThat(equalTo("bonjour")))); synch1.delete(def); definitions = synch2.findDefinitions(processor); assertThat(definitions, not(contains(pointsToContentsThat(equalTo("bonjour"))))); } private Matcher<ModuleDefinition> pointsToContentsThat(final Matcher<String> delegate) { return new DiagnosingMatcher<ModuleDefinition>() { @Override protected boolean matches(Object item, Description mismatchDescription) { if (item instanceof SimpleModuleDefinition) { SimpleModuleDefinition simpleModuleDefinition = (SimpleModuleDefinition) item; try { InputStream is = resolver.getResource(simpleModuleDefinition.getLocation()).getInputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); FileCopyUtils.copy(is, bos); delegate.describeMismatch(bos.toString(), mismatchDescription); return delegate.matches(bos.toString()); } catch (IOException e) { throw new RuntimeException(e); } } else { mismatchDescription.appendText(" is not a " + SimpleModuleDefinition.class.getSimpleName()); return false; } } @Override public void describeTo(Description description) { description.appendText("a module definition with contents ").appendDescriptionOf(delegate); } }; } }