001/** 002 * Copyright 2015 DuraSpace, Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fcrepo.camel.audit.triplestore; 017 018import static org.fcrepo.audit.AuditNamespaces.AUDIT; 019import static org.fcrepo.audit.AuditNamespaces.PREMIS; 020import static org.fcrepo.audit.AuditNamespaces.PROV; 021import static org.fcrepo.camel.RdfNamespaces.RDF; 022import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDdateTime; 023import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDstring; 024import static com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel; 025import static com.hp.hpl.jena.rdf.model.ResourceFactory.createProperty; 026import static com.hp.hpl.jena.rdf.model.ResourceFactory.createResource; 027import static com.hp.hpl.jena.rdf.model.ResourceFactory.createTypedLiteral; 028 029import java.io.ByteArrayOutputStream; 030import java.io.IOException; 031import java.text.DateFormat; 032import java.text.SimpleDateFormat; 033import java.util.Date; 034import java.util.TimeZone; 035 036import org.fcrepo.audit.AuditUtils; 037import org.fcrepo.camel.JmsHeaders; 038import org.fcrepo.camel.processor.ProcessorUtils; 039 040import org.apache.camel.Exchange; 041import org.apache.camel.Message; 042import org.apache.camel.Processor; 043import com.hp.hpl.jena.rdf.model.Model; 044import com.hp.hpl.jena.rdf.model.Property; 045import com.hp.hpl.jena.rdf.model.Resource; 046 047/** 048 * A processor that converts an audit message into a sparql-update 049 * statement for an external triplestore. 050 * 051 * @author Aaron Coburn 052 * @author escowles 053 * @since 2015-04-09 054 */ 055 056public class AuditSparqlProcessor implements Processor { 057 058 /** 059 * Define how a message should be processed. 060 * 061 * @param exchange the current camel message exchange 062 */ 063 public void process(final Exchange exchange) throws Exception { 064 final Message in = exchange.getIn(); 065 final String eventURIBase = in.getHeader(AuditHeaders.EVENT_BASE_URI, String.class); 066 final String eventID = in.getHeader(JmsHeaders.EVENT_ID, String.class); 067 final Resource eventURI = createResource(eventURIBase + "/" + eventID); 068 069 // generate SPARQL Update 070 final StringBuilder query = new StringBuilder("update="); 071 query.append(ProcessorUtils.insertData(serializedGraphForMessage(in, eventURI), null)); 072 073 // update exchange 074 in.setBody(query.toString()); 075 in.setHeader(AuditHeaders.EVENT_URI, eventURI.toString()); 076 in.setHeader(Exchange.CONTENT_TYPE, "application/x-www-form-urlencoded"); 077 in.setHeader(Exchange.HTTP_METHOD, "POST"); 078 } 079 080 // namespaces and properties 081 private static final Resource INTERNAL_EVENT = createResource(AUDIT + "InternalEvent"); 082 private static final Resource PREMIS_EVENT = createResource(PREMIS + "Event"); 083 private static final Resource PROV_EVENT = createResource(PROV + "InstantaneousEvent"); 084 085 private static final Property PREMIS_TIME = createProperty(PREMIS + "hasEventDateTime"); 086 private static final Property PREMIS_OBJ = createProperty(PREMIS + "hasEventRelatedObject"); 087 private static final Property PREMIS_AGENT = createProperty(PREMIS + "hasEventRelatedAgent"); 088 private static final Property PREMIS_TYPE = createProperty(PREMIS + "hasEventType"); 089 private static final Property RDF_TYPE = createProperty(RDF + "type"); 090 091 private static final String EMPTY_STRING = ""; 092 093 /** 094 * Convert a Camel message to audit event description. 095 * @param message Camel message produced by an audit event 096 * @param subject RDF subject of the audit description 097 */ 098 private static String serializedGraphForMessage(final Message message, final Resource subject) throws IOException { 099 100 // serialize triples 101 final ByteArrayOutputStream serializedGraph = new ByteArrayOutputStream(); 102 final Model model = createDefaultModel(); 103 104 // get info from jms message headers 105 final String eventType = (String) message.getHeader(JmsHeaders.EVENT_TYPE, EMPTY_STRING); 106 final Long timestamp = (Long) message.getHeader(JmsHeaders.TIMESTAMP, 0); 107 final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 108 df.setTimeZone(TimeZone.getTimeZone("UTC")); 109 final String date = df.format(new Date(timestamp)); 110 final String user = (String) message.getHeader(JmsHeaders.USER, EMPTY_STRING); 111 final String agent = (String) message.getHeader(JmsHeaders.USER_AGENT, EMPTY_STRING); 112 final String properties = (String) message.getHeader(JmsHeaders.PROPERTIES, EMPTY_STRING); 113 final String identifier = ProcessorUtils.getSubjectUri(message); 114 final String premisType = AuditUtils.getAuditEventType(eventType, properties); 115 116 model.add( model.createStatement(subject, RDF_TYPE, INTERNAL_EVENT) ); 117 model.add( model.createStatement(subject, RDF_TYPE, PREMIS_EVENT) ); 118 model.add( model.createStatement(subject, RDF_TYPE, PROV_EVENT) ); 119 120 // basic event info 121 model.add( model.createStatement(subject, PREMIS_TIME, createTypedLiteral(date, XSDdateTime)) ); 122 model.add( model.createStatement(subject, PREMIS_OBJ, createResource(identifier)) ); 123 model.add( model.createStatement(subject, PREMIS_AGENT, createTypedLiteral(user, XSDstring)) ); 124 model.add( model.createStatement(subject, PREMIS_AGENT, createTypedLiteral(agent, XSDstring)) ); 125 if (premisType != null) { 126 model.add(model.createStatement(subject, PREMIS_TYPE, createResource(premisType))); 127 } 128 129 model.write(serializedGraph, "N-TRIPLE"); 130 return serializedGraph.toString("UTF-8"); 131 } 132}