edu.artic.geneva.sync.RowProcessor.java Source code

Java tutorial

Introduction

Here is the source code for edu.artic.geneva.sync.RowProcessor.java

Source

/*
 * 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.artic.geneva.sync;

import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
import static org.fcrepo.camel.FcrepoHeaders.FCREPO_IDENTIFIER;

import java.io.IOException;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import org.apache.camel.RuntimeCamelException;

/**
 * Represents a processor for processing a single database row and
 * converting it into a form that is ready to be translated into JSON-LD.
 *
 * @author Aaron Coburn
 */
class RowProcessor implements Processor {

    public static final int DEFAULT_COUNT = 4;
    public static final int DEFAULT_LENGTH = 2;

    public void process(final Exchange exchange) throws IOException {

        final Message in = exchange.getIn();
        final CamelContext ctx = exchange.getContext();

        final Map<String, Object> row = in.getBody(Map.class);
        final String idField;
        final String baseUrl;
        final String jsonLdContext;
        final String prefix;
        final List<String> extras;
        final List<String> types;
        final List<String> managedTypes;
        final List<String> managedFields;

        try {
            idField = ctx.resolvePropertyPlaceholders("{{geneva.idField}}");
            jsonLdContext = ctx.resolvePropertyPlaceholders("{{geneva.jsonld}}");
            baseUrl = ctx.resolvePropertyPlaceholders("{{fcrepo.baseUrl}}");
            prefix = ctx.resolvePropertyPlaceholders("{{fcrepo.pathPrefix}}");
            extras = asList(ctx.resolvePropertyPlaceholders("{{geneva.extraProps}}").split(";"));
            types = asList(ctx.resolvePropertyPlaceholders("{{geneva.defaultTypes}}").split(","));
            managedFields = asList(ctx.resolvePropertyPlaceholders("{{geneva.managedFields}}").split(","));
            managedTypes = asList(ctx.resolvePropertyPlaceholders("{{geneva.managedTypes}}").split(","));
        } catch (final Exception ex) {
            throw new RuntimeCamelException("Could not resolve properties", ex);
        }

        final String id = row.get(idField).toString();
        final String path = buildPath(prefix, id);

        // Convert any Timestamp to ISO 8601 String representation
        final Map<String, Object> updates = new HashMap<>();
        for (final Map.Entry<String, Object> e : row.entrySet()) {
            if (e.getValue() instanceof Timestamp) {
                final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
                updates.put(e.getKey(), df.format((Timestamp) e.getValue()));
            }
        }
        row.putAll(updates);

        // Set the RDF base context
        row.put("@id", (baseUrl.startsWith("http") ? "" : "http://") + baseUrl + path);

        // Add the JSON-LD context
        if (jsonLdContext != null) {
            row.put("@context", jsonLdContext);
        }

        // Gather any managed types
        final List<String> supplemental = new ArrayList<>();
        if (managedFields.size() == managedTypes.size()) {
            for (int i = 0; i < managedFields.size(); i++) {
                final Object val = row.get(managedFields.get(i));
                if (val != null && val instanceof Boolean) {
                    if ((Boolean) val && managedTypes.get(i) != null) {
                        supplemental.add((String) managedTypes.get(i));
                    }
                }
            }
        }

        // Add any default or managed RDF Types
        final List<String> filtered = Stream.concat(types.stream(), supplemental.stream())
                .filter(x -> x.length() > 0).collect(toList());
        if (filtered.size() > 0) {
            row.put("@type", filtered);
        }

        // Add any additional RDF properties (provided they don't overwrite existing values)
        for (final String extra : extras) {
            final List<String> parts = asList(extra.split(","));
            if (parts.size() == 2 && !row.containsKey(parts.get(0))) {
                row.put(parts.get(0), parts.get(1));
            }
        }

        in.setHeader(FCREPO_IDENTIFIER, path);
    }

    /**
     *  Mint a URL for the given ID value, based on an MD5-based pairtree
     */
    private static String buildPath(final String prefix, final String id) {

        final StringBuilder path = new StringBuilder();
        final String md5 = md5Hex(id);

        final StringJoiner joiner = new StringJoiner("/", "", "/" + md5);

        IntStream.rangeClosed(0, DEFAULT_COUNT - 1)
                .forEach(x -> joiner.add(md5.substring(x * DEFAULT_LENGTH, (x + 1) * DEFAULT_LENGTH)));

        if (!prefix.startsWith("/")) {
            path.append("/");
        }

        if (prefix.length() > 0) {
            path.append(prefix);

            if (!prefix.endsWith("/")) {
                path.append("/");
            }
        }

        path.append(joiner.toString());
        return path.toString();
    }
}