All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.amherst.acdc.exts.image.ImageRouter Maven / Gradle / Ivy

/*
 * Copyright Amherst College
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package edu.amherst.acdc.exts.image;

import static java.util.Arrays.stream;
import static org.apache.camel.Exchange.CONTENT_TYPE;
import static org.apache.camel.Exchange.HTTP_METHOD;
import static org.apache.camel.Exchange.HTTP_RESPONSE_CODE;
import static org.apache.camel.Exchange.HTTP_URI;
import static org.apache.camel.LoggingLevel.INFO;
import static org.apache.camel.builder.PredicateBuilder.and;
import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_ARGS;
import static org.slf4j.LoggerFactory.getLogger;

import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;

import org.apache.camel.Exchange;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.builder.RouteBuilder;
import org.slf4j.Logger;

/**
 * A content router for handling image conversion requests.
 *
 * @author Aaron Coburn
 */
public class ImageRouter extends RouteBuilder {

    private static final String FEDORA_BASE_URL = "CamelFedoraBaseUrl";
    private static final String FEDORA_URI = "CamelFedoraUri";
    private static final String IMAGE_OUTPUT = "CamelImageOutput";
    private static final String IMAGE_INPUT = "CamelImageInput";
    private static final String HTTP_ACCEPT = "Accept";
    private static final String HTTP_QUERY_OPTIONS = "options";
    private static final String HTTP_QUERY_CONTEXT = "context";

    private static final Logger LOGGER = getLogger(ImageRouter.class);

    /**
     * Configure the message route workflow.
     */
    public void configure() throws Exception {

        from("jetty:http://{{rest.host}}:{{rest.port}}{{rest.prefix}}?" +
              "sendServerVersion=false&" +
              "httpMethodRestrict=GET,OPTIONS").routeId("ImageRouter")
          .process(e -> e.getIn().setHeader(FEDORA_URI,
                  e.getIn().getHeader(HTTP_QUERY_CONTEXT,
                      e.getIn().getHeader("Apix-Ldp-Resource"))))
          .setHeader(FEDORA_BASE_URL).simple("{{fcrepo.baseUrl}}")
          .setHeader(IMAGE_OUTPUT).header(HTTP_ACCEPT)
          .removeHeaders(HTTP_ACCEPT)
          .choice()
            .when(and(header(HTTP_METHOD).isEqualTo("GET"), header(FEDORA_URI).startsWith(header(FEDORA_BASE_URL))))
              .to("direct:get")
            .when(header(HTTP_METHOD).isEqualTo("OPTIONS"))
              .setHeader(CONTENT_TYPE).constant("text/turtle")
              .setHeader(HTTP_ACCEPT).constant("GET,OPTIONS")
              .to("language:simple:resource:classpath:options.ttl")
            .otherwise()
              .setHeader(HTTP_RESPONSE_CODE).constant(400)
              .setHeader(CONTENT_TYPE).constant("text/plain")
              .transform(constant("Missing/invalid context parameter"));

        from("direct:get").routeId("ImageGet")
          .setHeader(HTTP_URI).header(FEDORA_URI)
          .setHeader(HTTP_METHOD).constant("HEAD")
          .to("http4://localhost?authUsername={{fcrepo.authUsername}}" +
              "&authPassword={{fcrepo.authPassword}}&throwExceptionOnFailure=false")
          .choice()
            .when(header(CONTENT_TYPE).startsWith("image/"))
              .log(INFO, LOGGER, "Image Processing ${headers[CamelHttpPath]}")
              .to("direct:convert")
            .when(header("Link").contains(";rel=\"type\""))
              .setBody(constant("Error: this resource is not an image"))
              .to("direct:invalidFormat")
            .when(header(HTTP_RESPONSE_CODE).isEqualTo(200))
              .setBody(constant("Error: this resource is not an ldp:NonRDFSource"))
              .to("direct:invalidFormat")
            .otherwise()
              .to("direct:error");

         from("direct:invalidFormat").routeId("ImageInvalidFormat")
             .removeHeaders("*")
             .setHeader(CONTENT_TYPE).constant("text/plain")
             .setHeader(HTTP_RESPONSE_CODE).constant(400);

         from("direct:error").routeId("ImageError")
             .setBody(constant("Error: this resource is not accessible"))
             .setHeader(CONTENT_TYPE).constant("text/plain");

        from("direct:convert").routeId("ImageConvert")
          .setHeader(HTTP_METHOD).constant("GET")
          .setHeader(HTTP_URI).header(FEDORA_URI)
          .to("http4://localhost?authUsername={{fcrepo.authUsername}}" +
              "&authPassword={{fcrepo.authPassword}}&throwExceptionOnFailure=true")
          .setHeader(IMAGE_INPUT).header(CONTENT_TYPE)
          .process(exchange -> {
              final String accept = exchange.getIn().getHeader(IMAGE_OUTPUT, "", String.class);
              final String fmt;
              final boolean valid;
              try {
                  if (accept.matches("^image/\\w+$")) {
                      fmt = accept.replace("image/", "");
                  } else {
                      fmt = getContext().resolvePropertyPlaceholders("{{default.format}}");
                  }
                  valid = stream(getContext().resolvePropertyPlaceholders("{{valid.formats}}").split(","))
                                            .anyMatch(fmt::equals);
              } catch (final Exception ex) {
                  throw new RuntimeCamelException("Couldn't resolve property placeholder", ex);
              }

              if (valid) {
                  exchange.getIn().setHeader(IMAGE_OUTPUT, "image/" + fmt);
                  exchange.getIn().setHeader(EXEC_COMMAND_ARGS,
                      " - " + cmdOptions(exchange) + " " + fmt + ":-");
              } else {
                  throw new RuntimeCamelException("Invalid format: " + fmt);
              }
          })
          .removeHeaders("CamelHttp*")
          .log(INFO, LOGGER, "Converting from ${headers[CamelImageInput]} to ${headers[CamelImageOutput]}")
          .to("exec:{{convert.path}}")
          .process(exchange ->
              exchange.getOut().setBody(exchange.getIn().getBody(InputStream.class)));
    }

    // Hack - cmdline options are often an array of repeated elements.  Fix that
    @SuppressWarnings("unchecked")
    static final String cmdOptions(final Exchange e) {
        final Object optHdr = e.getIn().getHeader(HTTP_QUERY_OPTIONS);

        if (optHdr instanceof Collection) {
            return String.join(" ",  new HashSet<>((Collection) optHdr));
        } else if (optHdr != null) {
            return (String) optHdr;
        }
        return "";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy