001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.camel.indexing.triplestore;
019
020import static java.util.stream.Collectors.toList;
021import static org.apache.camel.builder.PredicateBuilder.in;
022import static org.apache.camel.builder.PredicateBuilder.not;
023import static org.apache.camel.builder.PredicateBuilder.or;
024import static org.fcrepo.camel.FcrepoHeaders.FCREPO_NAMED_GRAPH;
025import static org.fcrepo.camel.FcrepoHeaders.FCREPO_EVENT_TYPE;
026import static org.fcrepo.camel.FcrepoHeaders.FCREPO_URI;
027import static org.fcrepo.camel.processor.ProcessorUtils.tokenizePropertyPlaceholder;
028import static org.slf4j.LoggerFactory.getLogger;
029
030import org.apache.camel.LoggingLevel;
031import org.apache.camel.builder.RouteBuilder;
032import org.apache.camel.builder.xml.Namespaces;
033import org.apache.camel.builder.xml.XPathBuilder;
034import org.fcrepo.camel.processor.EventProcessor;
035import org.fcrepo.camel.processor.SparqlDeleteProcessor;
036import org.fcrepo.camel.processor.SparqlUpdateProcessor;
037import org.slf4j.Logger;
038
039/**
040 * A content router for handling Fedora events.
041 *
042 * @author Aaron Coburn
043 */
044public class TriplestoreRouter extends RouteBuilder {
045
046    private static final Logger LOGGER = getLogger(TriplestoreRouter.class);
047
048    private static final String RESOURCE_DELETION = "http://fedora.info/definitions/v4/event#ResourceDeletion";
049    private static final String DELETE = "https://www.w3.org/ns/activitystreams#Delete";
050
051    /**
052     * Configure the message route workflow.
053     */
054    @Override
055    public void configure() throws Exception {
056
057        final Namespaces ns = new Namespaces("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
058        ns.add("indexing", "http://fedora.info/definitions/v4/indexing#");
059
060        final XPathBuilder indexable = new XPathBuilder(
061                String.format("/rdf:RDF/rdf:Description/rdf:type[@rdf:resource='%s']",
062                    "http://fedora.info/definitions/v4/indexing#Indexable"));
063        indexable.namespaces(ns);
064
065        /**
066         * A generic error handler (specific to this RouteBuilder)
067         */
068        onException(Exception.class)
069            .maximumRedeliveries("{{error.maxRedeliveries}}")
070            .log("Index Routing Error: ${routeId}");
071
072        /**
073         * route a message to the proper queue, based on whether
074         * it is a DELETE or UPDATE operation.
075         */
076        from("{{input.stream}}")
077            .routeId("FcrepoTriplestoreRouter")
078            .process(new EventProcessor())
079            .choice()
080                .when(or(header(FCREPO_EVENT_TYPE).contains(RESOURCE_DELETION),
081                            header(FCREPO_EVENT_TYPE).contains(DELETE)))
082                    .to("direct:delete.triplestore")
083                .otherwise()
084                    .to("direct:index.triplestore");
085
086        /**
087         * Handle re-index events
088         */
089        from("{{triplestore.reindex.stream}}")
090            .routeId("FcrepoTriplestoreReindex")
091            .to("direct:index.triplestore");
092
093        /**
094         * Based on an item's metadata, determine if it is indexable.
095         */
096        from("direct:index.triplestore")
097            .routeId("FcrepoTriplestoreIndexer")
098            .filter(not(in(tokenizePropertyPlaceholder(getContext(), "{{filter.containers}}", ",").stream()
099                        .map(uri -> or(
100                            header(FCREPO_URI).startsWith(constant(uri + "/")),
101                            header(FCREPO_URI).isEqualTo(constant(uri))))
102                        .collect(toList()))))
103            .removeHeaders("CamelHttp*")
104            .choice()
105                .when(simple("{{indexing.predicate}} != 'true'"))
106                    .to("direct:update.triplestore")
107                .otherwise()
108                    .to("fcrepo:{{fcrepo.baseUrl}}?preferInclude=PreferMinimalContainer&accept=application/rdf+xml")
109                    .choice()
110                        .when(indexable)
111                            .to("direct:update.triplestore")
112                        .otherwise()
113                            .to("direct:delete.triplestore");
114
115        /**
116         * Remove an item from the triplestore index.
117         */
118        from("direct:delete.triplestore")
119            .routeId("FcrepoTriplestoreDeleter")
120            .process(new SparqlDeleteProcessor())
121            .log(LoggingLevel.INFO, LOGGER,
122                    "Deleting Triplestore Object ${headers[CamelFcrepoUri]}")
123            .to("{{triplestore.baseUrl}}?useSystemProperties=true");
124
125        /**
126         * Perform the sparql update.
127         */
128        from("direct:update.triplestore")
129            .routeId("FcrepoTriplestoreUpdater")
130            .setHeader(FCREPO_NAMED_GRAPH)
131                .simple("{{triplestore.namedGraph}}")
132            .to("fcrepo:{{fcrepo.baseUrl}}?accept=application/n-triples" +
133                    "&preferOmit={{prefer.omit}}&preferInclude={{prefer.include}}")
134            .process(new SparqlUpdateProcessor())
135            .log(LoggingLevel.INFO, LOGGER,
136                    "Indexing Triplestore Object ${headers[CamelFcrepoUri]}")
137            .to("{{triplestore.baseUrl}}?useSystemProperties=true");
138    }
139}