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}