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 public void configure() throws Exception { 055 056 final Namespaces ns = new Namespaces("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); 057 ns.add("indexing", "http://fedora.info/definitions/v4/indexing#"); 058 059 final XPathBuilder indexable = new XPathBuilder( 060 String.format("/rdf:RDF/rdf:Description/rdf:type[@rdf:resource='%s']", 061 "http://fedora.info/definitions/v4/indexing#Indexable")); 062 indexable.namespaces(ns); 063 064 /** 065 * A generic error handler (specific to this RouteBuilder) 066 */ 067 onException(Exception.class) 068 .maximumRedeliveries("{{error.maxRedeliveries}}") 069 .log("Index Routing Error: ${routeId}"); 070 071 /** 072 * route a message to the proper queue, based on whether 073 * it is a DELETE or UPDATE operation. 074 */ 075 from("{{input.stream}}") 076 .routeId("FcrepoTriplestoreRouter") 077 .process(new EventProcessor()) 078 .choice() 079 .when(or(header(FCREPO_EVENT_TYPE).contains(RESOURCE_DELETION), 080 header(FCREPO_EVENT_TYPE).contains(DELETE))) 081 .to("direct:delete.triplestore") 082 .otherwise() 083 .to("direct:index.triplestore"); 084 085 /** 086 * Handle re-index events 087 */ 088 from("{{triplestore.reindex.stream}}") 089 .routeId("FcrepoTriplestoreReindex") 090 .to("direct:index.triplestore"); 091 092 /** 093 * Based on an item's metadata, determine if it is indexable. 094 */ 095 from("direct:index.triplestore") 096 .routeId("FcrepoTriplestoreIndexer") 097 .filter(not(in(tokenizePropertyPlaceholder(getContext(), "{{filter.containers}}", ",").stream() 098 .map(uri -> or( 099 header(FCREPO_URI).startsWith(constant(uri + "/")), 100 header(FCREPO_URI).isEqualTo(constant(uri)))) 101 .collect(toList())))) 102 .removeHeaders("CamelHttp*") 103 .to("fcrepo:{{fcrepo.baseUrl}}?preferInclude=PreferMinimalContainer&accept=application/rdf+xml") 104 .choice() 105 .when(or(simple("{{indexing.predicate}} != 'true'"), indexable)) 106 .to("direct:update.triplestore") 107 .otherwise() 108 .to("direct:delete.triplestore"); 109 110 /** 111 * Remove an item from the triplestore index. 112 */ 113 from("direct:delete.triplestore") 114 .routeId("FcrepoTriplestoreDeleter") 115 .process(new SparqlDeleteProcessor()) 116 .log(LoggingLevel.INFO, LOGGER, 117 "Deleting Triplestore Object ${headers[CamelFcrepoUri]}") 118 .to("{{triplestore.baseUrl}}?useSystemProperties=true"); 119 120 /** 121 * Perform the sparql update. 122 */ 123 from("direct:update.triplestore") 124 .routeId("FcrepoTriplestoreUpdater") 125 .setHeader(FCREPO_NAMED_GRAPH) 126 .simple("{{triplestore.namedGraph}}") 127 .to("fcrepo:{{fcrepo.baseUrl}}?accept=application/n-triples" + 128 "&preferOmit={{prefer.omit}}&preferInclude={{prefer.include}}") 129 .process(new SparqlUpdateProcessor()) 130 .log(LoggingLevel.INFO, LOGGER, 131 "Indexing Triplestore Object ${headers[CamelFcrepoUri]}") 132 .to("{{triplestore.baseUrl}}?useSystemProperties=true"); 133 } 134}