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 */ 018 019package org.fcrepo.camel.processor; 020 021import static java.util.Collections.singletonList; 022import static java.util.Optional.empty; 023import static java.util.Optional.of; 024import static java.util.Optional.ofNullable; 025import static java.util.stream.Collectors.toList; 026import static org.fcrepo.camel.FcrepoHeaders.FCREPO_AGENT; 027import static org.fcrepo.camel.FcrepoHeaders.FCREPO_DATE_TIME; 028import static org.fcrepo.camel.FcrepoHeaders.FCREPO_EVENT_ID; 029import static org.fcrepo.camel.FcrepoHeaders.FCREPO_EVENT_TYPE; 030import static org.fcrepo.camel.FcrepoHeaders.FCREPO_RESOURCE_TYPE; 031import static org.fcrepo.camel.FcrepoHeaders.FCREPO_URI; 032 033import java.io.InputStream; 034import java.io.IOException; 035import java.util.ArrayList; 036import java.util.HashMap; 037import java.util.HashSet; 038import java.util.List; 039import java.util.Map; 040import java.util.Optional; 041import java.util.Set; 042 043import com.fasterxml.jackson.databind.JsonNode; 044import com.fasterxml.jackson.databind.ObjectMapper; 045 046import org.apache.camel.Exchange; 047import org.apache.camel.Processor; 048 049/** 050 * Converts a Fedora Message into camel-based headers. 051 * 052 * @author acoburn 053 */ 054public class EventProcessor implements Processor { 055 056 private static final ObjectMapper mapper = new ObjectMapper(); 057 058 /** 059 * Process the Fedora message 060 * 061 * @param exchange the current camel message exchange 062 */ 063 public void process(final Exchange exchange) throws IOException { 064 final Object body = exchange.getIn().getBody(); 065 final Map<String, List<String>> data = new HashMap<>(); 066 if (body != null) { 067 // In the event that the message was already converted to a Map 068 if (body instanceof Map) { 069 data.putAll(getValuesFromMap((Map)body)); 070 } else if (body instanceof String) { 071 data.putAll(getValuesFromJson(mapper.readTree((String)body))); 072 } else if (body instanceof InputStream) { 073 data.putAll(getValuesFromJson(mapper.readTree((InputStream)body))); 074 } 075 } 076 077 final Set<String> singleValuedFields = new HashSet<String>(); 078 singleValuedFields.add(FCREPO_URI); 079 singleValuedFields.add(FCREPO_DATE_TIME); 080 singleValuedFields.add(FCREPO_EVENT_ID); 081 082 data.entrySet().stream().filter(entry -> entry.getValue() != null) 083 .filter(entry -> !entry.getValue().isEmpty()).forEach(entry -> { 084 if (singleValuedFields.contains(entry.getKey())) { 085 exchange.getIn().setHeader(entry.getKey(), entry.getValue().get(0)); 086 } else { 087 exchange.getIn().setHeader(entry.getKey(), entry.getValue()); 088 } 089 }); 090 } 091 092 private Map<String, List<String>> getValuesFromJson(final JsonNode body) { 093 final Map<String, List<String>> data = new HashMap<>(); 094 095 getValues(body, "@id").ifPresent(id -> data.put(FCREPO_EVENT_ID, id)); 096 getValues(body, "id").ifPresent(id -> data.putIfAbsent(FCREPO_EVENT_ID, id)); 097 098 getValues(body, "published").ifPresent(date -> data.putIfAbsent(FCREPO_DATE_TIME, date)); 099 getValues(body, "type").map(EventProcessor::toUris).ifPresent(type -> data.put(FCREPO_EVENT_TYPE, type)); 100 101 if (body.has("object")) { 102 final JsonNode object = body.get("object"); 103 getValues(object, "@id").ifPresent(id -> data.put(FCREPO_URI, id)); 104 getValues(object, "id").ifPresent(id -> data.putIfAbsent(FCREPO_URI, id)); 105 106 getValues(object, "@type").ifPresent(type -> data.put(FCREPO_RESOURCE_TYPE, type)); 107 getValues(object, "type").ifPresent(type -> data.putIfAbsent(FCREPO_RESOURCE_TYPE, type)); 108 } 109 110 if (body.has("actor")) { 111 final JsonNode actor = body.get("actor"); 112 if (actor.isArray()) { 113 final List<String> agents = new ArrayList<>(); 114 for (final JsonNode agent : actor) { 115 getString(agent, "name").ifPresent(agents::add); 116 getString(agent, "id").ifPresent(agents::add); 117 } 118 data.put(FCREPO_AGENT, agents); 119 } else { 120 getString(actor, "name").ifPresent(name -> data.put(FCREPO_AGENT, singletonList(name))); 121 getString(actor, "id").ifPresent(name -> data.put(FCREPO_AGENT, singletonList(name))); 122 } 123 } 124 return data; 125 } 126 127 private static List<String> toUris(final List<String> jsonldValues) { 128 return jsonldValues.stream().map(ActivityStreamTerms::expand).collect(toList()); 129 } 130 131 private static Optional<String> getString(final JsonNode node, final String fieldName) { 132 if (node.has(fieldName)) { 133 final JsonNode field = node.get(fieldName); 134 if (field.isTextual()) { 135 return of(field.asText()); 136 } 137 } 138 return empty(); 139 } 140 141 private static Optional<List<String>> getValues(final JsonNode node, final String fieldName) { 142 if (node.has(fieldName)) { 143 final JsonNode field = node.get(fieldName); 144 if (field.isArray()) { 145 final List<String> elements = new ArrayList<>(); 146 field.elements().forEachRemaining(elem -> { 147 if (elem.isTextual()) { 148 elements.add(elem.asText()); 149 } 150 }); 151 return of(elements); 152 } else if (field.isTextual()) { 153 return of(singletonList(field.asText())); 154 } 155 } 156 return empty(); 157 } 158 159 @SuppressWarnings("unchecked") 160 private static Map<String, List<String>> getValuesFromMap(final Map body) { 161 final Map<String, Object> values = (Map<String, Object>)body; 162 final Map<String, List<String>> data = new HashMap<>(); 163 if (values.containsKey("@id")) { 164 data.put(FCREPO_EVENT_ID, singletonList((String) values.get("@id"))); 165 } 166 if (values.containsKey("id")) { 167 data.putIfAbsent(FCREPO_EVENT_ID, singletonList((String) values.get("id"))); 168 } 169 170 if (values.containsKey("@type")) { 171 data.put(FCREPO_EVENT_TYPE, toUris((List<String>) values.get("@type"))); 172 } 173 if (values.containsKey("type")) { 174 data.putIfAbsent(FCREPO_EVENT_TYPE, toUris((List<String>) values.get("type"))); 175 } 176 177 if (values.containsKey("published")) { 178 data.putIfAbsent(FCREPO_DATE_TIME, singletonList((String) values.get("published"))); 179 } 180 181 final Map<String, Object> object = (Map<String, Object>) values.get("object"); 182 183 if (object != null) { 184 if (object.containsKey("type")) { 185 data.put(FCREPO_RESOURCE_TYPE, (List<String>) object.get("type")); 186 } 187 data.put(FCREPO_URI, singletonList((String) object.get("id"))); 188 } 189 190 final List<Map<String, String>> actor = (List<Map<String, String>>) values.get("actor"); 191 if (actor != null) { 192 data.put(FCREPO_AGENT, 193 actor.stream().map(agent -> ofNullable(agent.get("name")).orElse(agent.get("id"))) 194 .collect(toList())); 195 } 196 197 return data; 198 } 199}