001/*
002 * The contents of this file are subject to the license and copyright
003 * detailed in the LICENSE and NOTICE files at the root of the source
004 * tree.
005 */
006package org.fcrepo.camel.reindexing;
007
008import org.apache.camel.CamelContext;
009import org.apache.camel.EndpointInject;
010import org.apache.camel.Produce;
011import org.apache.camel.ProducerTemplate;
012import org.apache.camel.builder.AdviceWith;
013import org.apache.camel.builder.RouteBuilder;
014import org.apache.camel.component.mock.MockEndpoint;
015import org.apache.camel.model.ModelCamelContext;
016import org.apache.camel.model.TransformDefinition;
017import org.apache.camel.spring.javaconfig.CamelConfiguration;
018import org.apache.camel.util.ObjectHelper;
019import org.junit.BeforeClass;
020import org.junit.Test;
021import org.junit.runner.RunWith;
022import org.springframework.beans.factory.annotation.Autowired;
023import org.springframework.context.annotation.Bean;
024import org.springframework.context.annotation.ComponentScan;
025import org.springframework.context.annotation.Configuration;
026import org.springframework.test.annotation.DirtiesContext;
027import org.springframework.test.context.ContextConfiguration;
028import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
029import org.springframework.test.context.support.AnnotationConfigContextLoader;
030
031import java.net.InetAddress;
032import java.util.HashMap;
033import java.util.Map;
034
035import static org.apache.commons.lang3.StringUtils.isBlank;
036import static org.fcrepo.camel.FcrepoHeaders.FCREPO_URI;
037import static org.fcrepo.camel.reindexing.ReindexingHeaders.REINDEXING_RECIPIENTS;
038
039/**
040 * Test the route workflow.
041 *
042 * @author acoburn
043 * @since 2015-05-22
044 */
045@RunWith(SpringJUnit4ClassRunner.class)
046@ContextConfiguration(classes = {RouteTest.ContextConfig.class}, loader = AnnotationConfigContextLoader.class)
047public class RouteTest {
048
049    private static final String restPrefix = "/reindexing";
050    private static final String reindexingStream = "broker:queue:foo";
051    private static final String baseUrl = "http://localhost/rest";
052    private long ASSERT_PERIOD_MS = 5000;
053
054    @EndpointInject("mock:result")
055    protected MockEndpoint resultEndpoint;
056
057    @Produce("direct:start")
058    protected ProducerTemplate template;
059
060    @Autowired
061    private CamelContext camelContext;
062
063    @BeforeClass
064    public static void beforeClass() {
065        final String restPort = System.getProperty("fcrepo.dynamic.reindexing.port");
066        if (!isBlank(restPort)) {
067            System.setProperty("reindexing.stream", reindexingStream);
068            System.setProperty("reindexing.rest.port", restPort);
069            System.setProperty("fcrepo.baseUrl", baseUrl);
070
071        }
072        System.setProperty("reindexing.rest.prefix", restPrefix);
073    }
074
075    @DirtiesContext
076    @Test
077    public void testUsageRoute() throws Exception {
078
079        final String restPort = System.getProperty("fcrepo.dynamic.reindexing.port", "9080");
080
081        final var context = camelContext.adapt(ModelCamelContext.class);
082
083        AdviceWith.adviceWith(context, "FcrepoReindexingTraverse", a -> {
084            a.replaceFromWith("direct:traverse");
085            a.mockEndpointsAndSkip("broker:*");
086            a.mockEndpointsAndSkip("fcrepo:*");
087        });
088
089        AdviceWith.adviceWith(context, "FcrepoReindexingUsage", a -> {
090            a.weaveAddLast().to("mock:result");
091        });
092
093        resultEndpoint.expectedMessageCount(1);
094        resultEndpoint.message(0).body().contains("Fedora Reindexing Service");
095        resultEndpoint.message(0).body().contains(
096                InetAddress.getLocalHost().getHostName() + ":" + restPort + restPrefix);
097
098        template.sendBody("direct:usage", null);
099
100        MockEndpoint.assertIsSatisfied(resultEndpoint);
101    }
102
103    @DirtiesContext
104    @Test
105    public void testReindexNoEndpointsRoute() throws Exception {
106        final String url = "http://localhost:8080/fcrepo/rest/foo";
107
108        final var context = camelContext.adapt(ModelCamelContext.class);
109
110        AdviceWith.adviceWith(context, "FcrepoReindexingReindex", a -> {
111            a.mockEndpointsAndSkip(reindexingStream + "?disableTimeToLive=true");
112            a.weaveByType(TransformDefinition.class).after().to("mock:result");
113        });
114
115        AdviceWith.adviceWith(context, "FcrepoReindexingTraverse", a -> {
116            a.replaceFromWith("direct:traverse");
117            a.mockEndpointsAndSkip("broker:*");
118            a.mockEndpointsAndSkip("fcrepo:*");
119        });
120
121        final var reindexingEndpoint = MockEndpoint.resolve(camelContext, "mock:" + reindexingStream);
122        reindexingEndpoint.expectedMessageCount(0);
123        reindexingEndpoint.setAssertPeriod(ASSERT_PERIOD_MS);
124        resultEndpoint.expectedMessageCount(1);
125        resultEndpoint.expectedHeaderReceived(FCREPO_URI, url);
126        resultEndpoint.expectedBodiesReceived("No endpoints configured for indexing");
127
128        final Map<String, Object> headers = new HashMap<>();
129        headers.put(FCREPO_URI, url);
130        headers.put(REINDEXING_RECIPIENTS, "");
131
132        template.sendBodyAndHeaders("direct:reindex", null, headers);
133
134        MockEndpoint.assertIsSatisfied(resultEndpoint, reindexingEndpoint);
135    }
136
137    @DirtiesContext
138    @Test
139    public void testReindexWithEndpointsRoute() throws Exception {
140        final String url = "http://localhost:8080/fcrepo/rest/foo";
141
142        final var context = camelContext.adapt(ModelCamelContext.class);
143
144        AdviceWith.adviceWith(context, "FcrepoReindexingReindex", a -> {
145            a.mockEndpointsAndSkip(reindexingStream + "?disableTimeToLive=true");
146            a.weaveByType(TransformDefinition.class).after().to("mock:result");
147        });
148
149        AdviceWith.adviceWith(context, "FcrepoReindexingTraverse", a -> {
150            a.replaceFromWith("direct:traverse");
151            a.mockEndpointsAndSkip("broker:*");
152            a.mockEndpointsAndSkip("fcrepo:*");
153        });
154
155        final var reindexingEndpoint = MockEndpoint.resolve(camelContext, "mock:" + reindexingStream);
156        reindexingEndpoint.expectedMessageCount(1);
157        resultEndpoint.expectedMessageCount(1);
158        resultEndpoint.expectedHeaderReceived(FCREPO_URI, url);
159        resultEndpoint.expectedBodiesReceived("Indexing started at " + url);
160
161        final Map<String, Object> headers = new HashMap<>();
162        headers.put(FCREPO_URI, url);
163        headers.put(REINDEXING_RECIPIENTS, "mock:endpoint");
164
165        template.sendBodyAndHeaders("direct:reindex", null, headers);
166
167        MockEndpoint.assertIsSatisfied(resultEndpoint, reindexingEndpoint);
168    }
169
170    @DirtiesContext
171    @Test
172    public void testTraversal() throws Exception {
173
174        final String baseUrl = "http://localhost:8080/fcrepo4/rest";
175
176        final var recipientsEndpoint = MockEndpoint.resolve(camelContext, "mock:direct:recipients");
177        final var reindexingEndpoint = MockEndpoint.resolve(camelContext, "mock:" + reindexingStream);
178
179        recipientsEndpoint.expectedMessageCount(1);
180        reindexingEndpoint.expectedMessageCount(7);
181        reindexingEndpoint.expectedHeaderValuesReceivedInAnyOrder(FCREPO_URI,
182                baseUrl + "/foo/a", baseUrl + "/foo/b", baseUrl + "/foo/c", baseUrl + "/foo/d", baseUrl + "/foo/e",
183                baseUrl + "/foo/f", baseUrl + "/foo/g");
184
185        final var context = camelContext.adapt(ModelCamelContext.class);
186
187        AdviceWith.adviceWith(context, "FcrepoReindexingTraverse", a -> {
188            a.replaceFromWith("direct:traverse");
189            a.mockEndpointsAndSkip("fcrepo:*");
190            a.mockEndpointsAndSkip(reindexingStream + "*");
191            a.mockEndpointsAndSkip("direct:recipients");
192        });
193
194        template.sendBodyAndHeader("direct:traverse", ObjectHelper.loadResourceAsStream("indexable.nt"),
195                FCREPO_URI, "http://localhost:8080/fcrepo4/rest/foo");
196
197        MockEndpoint.assertIsSatisfied(recipientsEndpoint, reindexingEndpoint);
198    }
199
200    @DirtiesContext
201    @Test
202    public void testRecipientList() throws Exception {
203        final String id = "/foo";
204
205
206        final var context = camelContext.adapt(ModelCamelContext.class);
207
208        AdviceWith.adviceWith(context, "FcrepoReindexingTraverse", a -> {
209            a.replaceFromWith("direct:traverse");
210            a.mockEndpointsAndSkip("broker:*");
211            a.mockEndpointsAndSkip("fcrepo:*");
212        });
213
214        final var fooEndpoint = MockEndpoint.resolve(camelContext, "mock:foo");
215        final var barEndpoint = MockEndpoint.resolve(camelContext, "mock:bar");
216
217        fooEndpoint.expectedMessageCount(1);
218        fooEndpoint.expectedHeaderReceived(FCREPO_URI, baseUrl + id);
219        barEndpoint.expectedMessageCount(1);
220        barEndpoint.expectedHeaderReceived(FCREPO_URI, baseUrl + id);
221
222        final Map<String, Object> headers = new HashMap<>();
223        headers.put(FCREPO_URI, baseUrl + id);
224        headers.put(REINDEXING_RECIPIENTS, "mock:foo,mock:bar");
225
226        template.sendBodyAndHeaders("direct:recipients", null, headers);
227
228        MockEndpoint.assertIsSatisfied(fooEndpoint, barEndpoint);
229    }
230
231    @Configuration
232    @ComponentScan(basePackages = {"org.fcrepo.camel"})
233    static class ContextConfig extends CamelConfiguration {
234
235        @Bean
236        public RouteBuilder route() {
237            return new ReindexingRouter();
238        }
239    }
240}