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}