Java tutorial
/* * 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(); } }