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}