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.reindexing; 007 008import org.apache.camel.ExchangePattern; 009import org.apache.camel.LoggingLevel; 010import org.apache.camel.builder.RouteBuilder; 011import org.fcrepo.camel.common.processor.DockerRunningProcessor; 012import org.fcrepo.camel.service.FcrepoCamelConfig; 013import org.slf4j.Logger; 014import org.springframework.beans.factory.annotation.Autowired; 015 016import static java.net.InetAddress.getLocalHost; 017import static org.apache.camel.Exchange.CONTENT_TYPE; 018import static org.apache.camel.Exchange.HTTP_METHOD; 019import static org.apache.camel.Exchange.HTTP_RESPONSE_CODE; 020import static org.apache.camel.LoggingLevel.INFO; 021import static org.fcrepo.camel.FcrepoHeaders.FCREPO_BASE_URL; 022import static org.fcrepo.camel.FcrepoHeaders.FCREPO_URI; 023import static org.fcrepo.camel.reindexing.ReindexingHeaders.REINDEXING_HOST; 024import static org.fcrepo.camel.reindexing.ReindexingHeaders.REINDEXING_PORT; 025import static org.fcrepo.camel.reindexing.ReindexingHeaders.REINDEXING_PREFIX; 026import static org.fcrepo.camel.reindexing.ReindexingHeaders.REINDEXING_RECIPIENTS; 027import static org.fcrepo.client.HttpMethods.GET; 028import static org.slf4j.LoggerFactory.getLogger; 029 030/** 031 * A content router for handling JMS events. 032 * 033 * @author Aaron Coburn 034 */ 035public class ReindexingRouter extends RouteBuilder { 036 037 private static final Logger LOGGER = getLogger(ReindexingRouter.class); 038 private static final int BAD_REQUEST = 400; 039 private static final String LDP_CONTAINS = "<http://www.w3.org/ns/ldp#contains>"; 040 041 @Autowired 042 private FcrepoReindexingConfig config; 043 044 @Autowired 045 private FcrepoCamelConfig fcrepoCamelConfig; 046 047 /** 048 * Configure the message route workflow. 049 */ 050 public void configure() throws Exception { 051 final String host = config.getRestHost(); 052 final String hostname = host.startsWith("http") ? host : "http://" + host; 053 final int port = config.getRestPort(); 054 /** 055 * A generic error handler (specific to this RouteBuilder) 056 */ 057 onException(Exception.class) 058 .maximumRedeliveries(config.getMaxRedeliveries()) 059 .log("Index Routing Error: ${routeId}"); 060 061 /** 062 * Expose a RESTful endpoint for re-indexing 063 */ 064 from("jetty:" + hostname + ":" + port + config.getRestPrefix() + 065 "?matchOnUriPrefix=true&httpMethodRestrict=GET,POST") 066 .routeId("FcrepoReindexingRest") 067 .routeDescription("Expose the reindexing endpoint over HTTP") 068 .setHeader(FCREPO_URI).simple(config.getFcrepoBaseUrl() + "${headers.CamelHttpPath}") 069 .choice() 070 .when(header(HTTP_METHOD).isEqualTo("GET")).to("direct:usage") 071 .otherwise().to("direct:reindex"); 072 073 from("direct:usage").routeId("FcrepoReindexingUsage") 074 .setHeader(REINDEXING_PREFIX).simple(config.getRestPrefix()) 075 .setHeader(REINDEXING_PORT).simple(String.valueOf(port)) 076 .setHeader(FCREPO_BASE_URL).simple(fcrepoCamelConfig.getFcrepoBaseUrl()) 077 .process(new DockerRunningProcessor()) 078 .process(exchange -> { 079 exchange.getIn().setHeader(REINDEXING_HOST, getLocalHost().getHostName()); 080 }) 081 .to("mustache:org/fcrepo/camel/reindexing/usage.mustache"); 082 083 /** 084 * A Re-indexing endpoint, setting where in the fcrepo hierarchy 085 * a re-indexing operation should begin. 086 */ 087 from("direct:reindex").routeId("FcrepoReindexingReindex") 088 .process(new RestProcessor()) 089 .removeHeaders("CamelHttp*") 090 .removeHeader("JMSCorrelationID") 091 .setBody(constant(null)) 092 .choice() 093 .when(header(HTTP_RESPONSE_CODE).isGreaterThanOrEqualTo(BAD_REQUEST)) 094 .endChoice() 095 .when(header(REINDEXING_RECIPIENTS).isEqualTo("")) 096 .transform().simple("No endpoints configured for indexing") 097 .endChoice() 098 .otherwise() 099 .log(INFO, LOGGER, "Initial indexing path: ${headers[CamelFcrepoUri]}") 100 .to(ExchangePattern.InOnly, config.getReindexingStream() + "?disableTimeToLive=true") 101 .setHeader(CONTENT_TYPE).constant("text/plain") 102 .transform().simple("Indexing started at ${headers[CamelFcrepoUri]}"); 103 104 /** 105 * A route that traverses through a fedora hierarchy 106 * indexing nodes, as appropriate. 107 */ 108 from(config.getReindexingStream() + "?asyncConsumer=true").routeId("FcrepoReindexingTraverse") 109 .to(ExchangePattern.InOnly, "direct:recipients") 110 .log(LoggingLevel.DEBUG, "Beginning traverse") 111 .removeHeaders("CamelHttp*") 112 .setHeader(HTTP_METHOD).constant(GET) 113 .to("fcrepo:" + fcrepoCamelConfig.getFcrepoBaseUrl() + "?preferInclude=PreferContainment" + 114 "&preferOmit=ServerManaged&accept=application/n-triples") 115 // split the n-triples stream on line breaks so that each triple is split into a separate message 116 .split(body().tokenize("\\n")).streaming() 117 .removeHeader(FCREPO_URI) 118 .removeHeader("JMSCorrelationID") 119 .process(exchange -> { 120 // This is a simple n-triples parser, spliting nodes on whitespace according to 121 // https://www.w3.org/TR/n-triples/#n-triples-grammar 122 // If the body is not null and the predicate is ldp:contains and the object is a URI, 123 // then set the CamelFcrepoUri header (if that header is not set, the processing stops 124 // at the filter() line below. 125 final String body = exchange.getIn().getBody(String.class); 126 if (body != null) { 127 final String parts[] = body.split("\\s+"); 128 if (parts.length > 2 && parts[1].equals(LDP_CONTAINS) && parts[2].startsWith("<")) { 129 exchange.getIn().setHeader(FCREPO_URI, parts[2].substring(1, parts[2].length() - 1)); 130 } 131 exchange.getIn().setBody(null); 132 } 133 }) 134 .filter(header(FCREPO_URI).isNotNull()) 135 .to(ExchangePattern.InOnly, config.getReindexingStream() + "?disableTimeToLive=true"); 136 137 /** 138 * Send the message to all of the pre-determined endpoints 139 */ 140 from("direct:recipients").routeId("FcrepoReindexingRecipients") 141 .recipientList(header(REINDEXING_RECIPIENTS)) 142 .ignoreInvalidEndpoints(); 143 } 144}