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}