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.indexing.triplestore;
007
008import org.apache.camel.LoggingLevel;
009import org.apache.camel.builder.RouteBuilder;
010import org.apache.camel.language.xpath.XPathBuilder;
011import org.apache.camel.support.builder.Namespaces;
012import org.fcrepo.camel.common.processor.AddBasicAuthProcessor;
013import org.fcrepo.camel.processor.EventProcessor;
014import org.fcrepo.camel.processor.SparqlDeleteProcessor;
015import org.fcrepo.camel.processor.SparqlUpdateProcessor;
016import org.slf4j.Logger;
017import org.springframework.beans.factory.annotation.Autowired;
018
019import static java.util.stream.Collectors.toList;
020import static org.apache.camel.builder.PredicateBuilder.in;
021import static org.apache.camel.builder.PredicateBuilder.not;
022import static org.apache.camel.builder.PredicateBuilder.or;
023import static org.fcrepo.camel.FcrepoHeaders.FCREPO_EVENT_TYPE;
024import static org.fcrepo.camel.FcrepoHeaders.FCREPO_NAMED_GRAPH;
025import static org.fcrepo.camel.FcrepoHeaders.FCREPO_URI;
026import static org.fcrepo.camel.processor.ProcessorUtils.tokenizePropertyPlaceholder;
027import static org.slf4j.LoggerFactory.getLogger;
028
029/**
030 * A content router for handling Fedora events.
031 *
032 * @author Aaron Coburn
033 */
034public class TriplestoreRouter extends RouteBuilder {
035
036    private static final Logger LOGGER = getLogger(TriplestoreRouter.class);
037
038    private static final String RESOURCE_DELETION = "http://fedora.info/definitions/v4/event#ResourceDeletion";
039    private static final String DELETE = "https://www.w3.org/ns/activitystreams#Delete";
040
041    @Autowired
042    private FcrepoTripleStoreIndexingConfig config;
043
044    /**
045     * Configure the message route workflow.
046     */
047    public void configure() throws Exception {
048
049        final Namespaces ns = new Namespaces("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
050        ns.add("indexing", "http://fedora.info/definitions/v4/indexing#");
051
052        final XPathBuilder indexable = new XPathBuilder(
053            String.format("/rdf:RDF/rdf:Description/rdf:type[@rdf:resource='%s']",
054                "http://fedora.info/definitions/v4/indexing#Indexable"));
055        indexable.namespaces(ns);
056
057        /**
058         * A generic error handler (specific to this RouteBuilder)
059         */
060        onException(Exception.class)
061            .maximumRedeliveries(config.getMaxRedeliveries())
062            .log("Index Routing Error: ${routeId}");
063
064        /**
065         * route a message to the proper queue, based on whether
066         * it is a DELETE or UPDATE operation.
067         */
068        from(config.getInputStream())
069            .routeId("FcrepoTriplestoreRouter")
070            .process(new EventProcessor())
071            .choice()
072            .when(or(header(FCREPO_EVENT_TYPE).contains(RESOURCE_DELETION),
073                header(FCREPO_EVENT_TYPE).contains(DELETE)))
074            .log(LoggingLevel.INFO, "deleting " + header(FCREPO_URI) + " from triplestore.")
075            .to("direct:delete.triplestore")
076            .otherwise()
077            .to("direct:index.triplestore");
078
079        /**
080         * Handle re-index events
081         */
082        from(config.getReindexStream())
083            .routeId("FcrepoTriplestoreReindex")
084            .to("direct:index.triplestore");
085
086        /**
087         * Based on an item's metadata, determine if it is indexable.
088         */
089        from("direct:index.triplestore")
090            .routeId("FcrepoTriplestoreIndexer")
091            .filter(not(in(tokenizePropertyPlaceholder(getContext(), config.getFilterContainers(), ",").stream()
092                .map(uri -> or(
093                    header(FCREPO_URI).startsWith(constant(uri + "/")),
094                    header(FCREPO_URI).isEqualTo(constant(uri))))
095                .collect(toList()))))
096            .removeHeaders("CamelHttp*")
097            .choice()
098            .when(simple(config.isIndexingPredicate() + " != 'true'"))
099            .to("direct:update.triplestore")
100            .otherwise()
101            .to("fcrepo:" + config.getFcrepoBaseUrl() +
102                "?preferInclude=PreferMinimalContainer&accept=application/rdf+xml")
103            .choice()
104            .when(indexable)
105            .to("direct:update.triplestore")
106            .otherwise()
107            .to("direct:delete.triplestore");
108
109        /**
110         * Remove an item from the triplestore index.
111         */
112        from("direct:delete.triplestore")
113            .routeId("FcrepoTriplestoreDeleter")
114            .process(new SparqlDeleteProcessor())
115            .log(LoggingLevel.INFO, LOGGER,
116                "Deleting Triplestore Object ${headers[CamelFcrepoUri]}")
117            .process(new AddBasicAuthProcessor(this.config.getTriplestoreAuthUsername(),
118                        this.config.getTriplestoreAuthPassword()))
119            .to(config.getTriplestoreBaseUrl() + "?useSystemProperties=true");
120
121        /**
122         * Perform the sparql update.
123         */
124        from("direct:update.triplestore")
125            .routeId("FcrepoTriplestoreUpdater")
126            .setHeader(FCREPO_NAMED_GRAPH)
127            .simple(config.getNamedGraph())
128            .to("fcrepo:" + config.getFcrepoBaseUrl() + "?accept=application/n-triples" +
129                "&preferOmit=" + config.getPreferOmit() + "&preferInclude=" + config.getPreferInclude())
130            .process(new SparqlUpdateProcessor())
131            .log(LoggingLevel.INFO, LOGGER,
132                "Indexing Triplestore Object ${headers[CamelFcrepoUri]}")
133            .process(new AddBasicAuthProcessor(this.config.getTriplestoreAuthUsername(),
134                    this.config.getTriplestoreAuthPassword()))
135            .to(config.getTriplestoreBaseUrl() + "?useSystemProperties=true");
136    }
137}