001/**
002 * Copyright 2015 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.fcrepo.camel.reindexing;
017
018import static org.fcrepo.camel.FcrepoHeaders.FCREPO_BASE_URL;
019import static org.slf4j.LoggerFactory.getLogger;
020
021import javax.xml.transform.stream.StreamSource;
022
023import org.apache.camel.Exchange;
024import org.apache.camel.LoggingLevel;
025import org.apache.camel.PropertyInject;
026import org.apache.camel.builder.RouteBuilder;
027import org.apache.camel.builder.xml.Namespaces;
028import org.fcrepo.client.HttpMethods;
029import org.fcrepo.camel.RdfNamespaces;
030import org.slf4j.Logger;
031
032/**
033 * A content router for handling JMS events.
034 *
035 * @author Aaron Coburn
036 */
037public class ReindexingRouter extends RouteBuilder {
038
039    private static final Logger LOGGER = getLogger(ReindexingRouter.class);
040    private static final int BAD_REQUEST = 400;
041
042    @PropertyInject(value = "rest.port", defaultValue = "9080")
043    private String port;
044
045    /**
046     * Configure the message route workflow.
047     */
048    public void configure() throws Exception {
049
050        restConfiguration().component("jetty").port(
051                System.getProperty("fcrepo.dynamic.reindexing.port", port));
052
053        final Namespaces ns = new Namespaces("rdf", RdfNamespaces.RDF);
054        ns.add("ldp", RdfNamespaces.LDP);
055
056        /**
057         * A generic error handler (specific to this RouteBuilder)
058         */
059        onException(Exception.class)
060            .maximumRedeliveries("{{error.maxRedeliveries}}")
061            .log("Index Routing Error: ${routeId}");
062
063        /**
064         * Expose a RESTful endpoint for re-indexing
065         */
066        rest("{{rest.prefix}}")
067            .get().to("direct:usage")
068            .post().consumes("application/json").to("direct:reindex");
069
070        from("direct:usage")
071            .routeId("FcrepoReindexingUsage")
072            .setHeader(ReindexingHeaders.REST_PREFIX).simple("{{rest.prefix}}")
073            .setHeader(ReindexingHeaders.REST_PORT).simple("{{rest.port}}")
074            .setHeader(FCREPO_BASE_URL).simple("{{fcrepo.baseUrl}}")
075            .process(new UsageProcessor());
076
077        /**
078         * A Re-indexing endpoint, setting where in the fcrepo hierarchy
079         * a re-indexing operation should begin.
080         */
081        from("direct:reindex")
082            .routeId("FcrepoReindexingReindex")
083            .setHeader(ReindexingHeaders.REST_PREFIX).simple("{{rest.prefix}}")
084            .setHeader(FCREPO_BASE_URL).simple("{{fcrepo.baseUrl}}")
085            .process(new RestProcessor())
086            .choice()
087                .when(header(Exchange.HTTP_RESPONSE_CODE).isGreaterThanOrEqualTo(BAD_REQUEST))
088                    .endChoice()
089                .when(header(ReindexingHeaders.RECIPIENTS).isEqualTo(""))
090                    .transform().simple("No endpoints configured for indexing")
091                    .endChoice()
092                .otherwise()
093                    .log(LoggingLevel.INFO, LOGGER, "Initial indexing path: ${headers[CamelFcrepoIdentifier]}")
094                    .inOnly("{{reindexing.stream}}?disableTimeToLive=true")
095                    .setHeader(Exchange.CONTENT_TYPE).constant("text/plain")
096                    .transform().simple("Indexing started at ${headers[CamelFcrepoIdentifier]}");
097
098        /**
099         *  A route that traverses through a fedora heirarchy
100         *  indexing nodes, as appropriate.
101         */
102        from("{{reindexing.stream}}?asyncConsumer=true")
103            .routeId("FcrepoReindexingTraverse")
104            .inOnly("direct:recipients")
105            .removeHeaders("CamelHttp*")
106            .setHeader(Exchange.HTTP_METHOD).constant(HttpMethods.GET)
107            .to("fcrepo:{{fcrepo.baseUrl}}?preferInclude=PreferContainment&preferOmit=ServerManaged")
108            .convertBodyTo(StreamSource.class)
109            .split().xtokenize("/rdf:RDF/rdf:Description/ldp:contains", 'i', ns).streaming()
110                .transform().xpath("/ldp:contains/@rdf:resource", String.class, ns)
111                .process(new PathProcessor())
112                .inOnly("{{reindexing.stream}}?disableTimeToLive=true");
113
114        /**
115         *  Send the message to all of the pre-determined endpoints
116         */
117        from("direct:recipients")
118            .routeId("FcrepoReindexingRecipients")
119            .recipientList(header(ReindexingHeaders.RECIPIENTS))
120            .ignoreInvalidEndpoints();
121    }
122}