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.serialization; 019 020import static java.net.URI.create; 021import static java.util.stream.Collectors.toList; 022import static org.apache.camel.LoggingLevel.INFO; 023import static org.apache.camel.LoggingLevel.DEBUG; 024import static org.apache.camel.Exchange.FILE_NAME; 025import static org.apache.camel.builder.PredicateBuilder.in; 026import static org.apache.camel.builder.PredicateBuilder.not; 027import static org.apache.camel.builder.PredicateBuilder.or; 028import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_ARGS; 029import static org.fcrepo.camel.FcrepoHeaders.FCREPO_EVENT_TYPE; 030import static org.fcrepo.camel.FcrepoHeaders.FCREPO_URI; 031import static org.fcrepo.camel.processor.ProcessorUtils.tokenizePropertyPlaceholder; 032 033import java.util.List; 034 035import org.apache.camel.Predicate; 036import org.apache.camel.builder.RouteBuilder; 037import org.apache.camel.builder.xml.Namespaces; 038import org.fcrepo.camel.processor.EventProcessor; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041 042/** 043 * 044 * A router for serializing fedora objects and binaries. 045 * 046 * @author Bethany Seeger 047 * @since 2015-09-28 048 */ 049 050public class SerializationRouter extends RouteBuilder { 051 052 private static final Logger LOGGER = LoggerFactory.getLogger(SerializationRouter.class); 053 054 private static final String RESOURCE_DELETION = "http://fedora.info/definitions/v4/event#ResourceDeletion"; 055 private static final String DELETE = "https://www.w3.org/ns/activitystreams#Delete"; 056 private static final String REPOSITORY = "http://fedora.info/definitions/v4/repository#"; 057 058 private static final String isBinaryResourceXPath = 059 "/rdf:RDF/rdf:Description/rdf:type[@rdf:resource=\"" + REPOSITORY + "Binary\"]"; 060 061 public static final String SERIALIZATION_PATH = "CamelSerializationPath"; 062 063 public final List<Predicate> uriFilter = tokenizePropertyPlaceholder(getContext(), "{{filter.containers}}", ",") 064 .stream().map(uri -> or( 065 header(FCREPO_URI).startsWith(constant(uri + "/")), 066 header(FCREPO_URI).isEqualTo(constant(uri)))) 067 .collect(toList()); 068 069 /** 070 * Configure the message route workflow 071 * 072 */ 073 074 public void configure() throws Exception { 075 076 final Namespaces ns = new Namespaces("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#") 077 .add("fedora", REPOSITORY); 078 079 /** 080 * A generic error handler (specific to this RouteBuilder) 081 */ 082 onException(Exception.class) 083 .maximumRedeliveries("{{error.maxRedeliveries}}") 084 .log("Index Routing Error: ${routeId}"); 085 086 /** 087 * Handle Serialization Events 088 */ 089 from("{{input.stream}}") 090 .routeId("FcrepoSerialization") 091 .process(new EventProcessor()) 092 .process(exchange -> { 093 final String uri = exchange.getIn().getHeader(FCREPO_URI, "", String.class); 094 exchange.getIn().setHeader(SERIALIZATION_PATH, create(uri).getPath()); 095 }) 096 .filter(not(in(uriFilter))) 097 .choice() 098 .when(or(header(FCREPO_EVENT_TYPE).contains(RESOURCE_DELETION), 099 header(FCREPO_EVENT_TYPE).contains(DELETE))) 100 .to("direct:delete") 101 .otherwise() 102 .multicast().to("direct:metadata", "direct:binary"); 103 104 from("{{serialization.stream}}") 105 .routeId("FcrepoReSerialization") 106 .filter(not(in(uriFilter))) 107 .process(exchange -> { 108 final String uri = exchange.getIn().getHeader(FCREPO_URI, "", String.class); 109 exchange.getIn().setHeader(SERIALIZATION_PATH, create(uri).getPath()); 110 }) 111 .multicast().to("direct:metadata", "direct:binary"); 112 113 from("direct:metadata") 114 .routeId("FcrepoSerializationMetadataUpdater") 115 .to("fcrepo:localhost?accept={{serialization.mimeType}}") 116 .log(INFO, LOGGER, "Serializing object ${headers[CamelFcrepoUri]}") 117 .setHeader(FILE_NAME).simple("${headers[CamelSerializationPath]}.{{serialization.extension}}") 118 .log(DEBUG, LOGGER, "filename is ${headers[CamelFileName]}") 119 .to("file://{{serialization.descriptions}}"); 120 121 from("direct:binary") 122 .routeId("FcrepoSerializationBinaryUpdater") 123 .filter().simple("{{serialization.includeBinaries}} == 'true'") 124 .to("fcrepo:localhost?preferInclude=PreferMinimalContainer" + 125 "&accept=application/rdf+xml") 126 .filter().xpath(isBinaryResourceXPath, ns) 127 .log(INFO, LOGGER, "Writing binary ${headers[CamelSerializationPath]}") 128 .to("fcrepo:localhost?metadata=false") 129 .setHeader(FILE_NAME).header(SERIALIZATION_PATH) 130 .log(DEBUG, LOGGER, "header filename is: ${headers[CamelFileName]}") 131 .to("file://{{serialization.binaries}}"); 132 133 from("direct:delete") 134 .routeId("FcrepoSerializationDeleter") 135 .setHeader(EXEC_COMMAND_ARGS).simple( 136 "-rf {{serialization.descriptions}}${headers[CamelSerializationPath]}.{{serialization.extension}} " + 137 "{{serialization.descriptions}}${headers[CamelSerializationPath]} " + 138 "{{serialization.binaries}}${headers[CamelSerializationPath]}") 139 .to("exec:rm"); 140 } 141}