Commit 59ee23b6 authored by acoburn's avatar acoburn
Browse files

jsonld compact/expand service

jsonld caching service
parent d7dbf077
......@@ -8,6 +8,8 @@ Services
* `acrepo-idiomatic`: Id Mapping Service: This maps a public ID to a (internal and typically much longer) fedora URI
* `acrepo-idiomatic-pgsql`: Id Mapping Service Database: This exposes a Postgres datastore for use with the Id Mapping service
* `acrepo-jsonld-cache`: This service exposes an HTTP endpoint for creating compact JSON-LD documents from a fedora repository using a pluggable context document
* `acrepo-jsonld-service`: This service creates expanded or compact JSON-LD representations of input documents
* `acrepo-mint-service`: This mints random (public) URIs for use with fedora resources
* `acrepo-xml-metadata`: This service translates Fedora RDF documents into MODS/XML or DC/XML
......@@ -28,6 +30,8 @@ command from its shell:
feature:repo-add mvn:edu.amherst.acdc/acrepo-karaf/LATEST/xml/features
feature:install acrepo-idiomatic
feature:install acrepo-idiomatic-pgsql
feature:install acrepo-jsonld-cache
feature:install acrepo-jsonld-service
feature:install acrepo-mint-service
feature:install acrepo-xml-metadata
......
Repository JSON-LD compaction service
=====================================
This collection of camel routes exposes an HTTP endpoint for
generating compact JSON-LD serializations of Fedora resources.
Building
--------
To build this project use
mvn install
Deploying in OSGi
-----------------
This projects can be deployed in an OSGi container. For example using
[Apache Karaf](http://karaf.apache.org) version 4.x or better, you can run the following
command from its shell:
feature:repo-add mvn:edu.amherst.acdc/acrepo-karaf/LATEST/xml/features
feature:install acrepo-jsonld-cache
Or by copying any of the compiled bundles into `$KARAF_HOME/deploy`.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>repository-services</artifactId>
<groupId>edu.amherst.acdc</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>acrepo-jsonld-cache</artifactId>
<packaging>bundle</packaging>
<name>JSON-LD AppCache Workflow</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-blueprint</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jetty9</artifactId>
</dependency>
<dependency>
<groupId>org.fcrepo.camel</groupId>
<artifactId>fcrepo-camel</artifactId>
</dependency>
<dependency>
<groupId>edu.amherst.acdc</groupId>
<artifactId>acrepo-jsonld-service</artifactId>
<version>${project.version}</version>
</dependency>
<!-- logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<!-- Testing & Camel Plugin -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-blueprint</artifactId>
</dependency>
</dependencies>
<build>
<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<!-- add configuration file to artifact set for OSGi deployment -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>src/main/cfg/edu.amherst.acdc.jsonld.cache.cfg</file>
<type>cfg</type>
<classifier>configuration</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
# Maximum redeliveries in case of error
error.maxRedeliveries=10
# HTTP Port
rest.port=13421
# JSON Context file
jsonld.context=https://acdc.amherst.edu/jsonld/context.json"
# Repository Base URL
fcrepo.baseUrl=localhost:8080/fcrepo/rest
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0 http://aries.apache.org/schemas/blueprint-cm/blueprint-cm-1.1.0.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<!-- OSGI blueprint property placeholder -->
<cm:property-placeholder persistent-id="edu.amherst.acdc.jsonld.cache" update-strategy="reload">
<cm:default-properties>
<cm:property name="error.maxRedeliveries" value="10"/>
<cm:property name="rest.port" value="13431"/>
<cm:property name="jsonld.context" value="https://acdc.amherst.edu/jsonld/context.json"/>
<cm:property name="fcrepo.baseUrl" value="localhost:8080/fcrepo/rest"/>
</cm:default-properties>
</cm:property-placeholder>
<reference id="jsonldService" interface="edu.amherst.acdc.jsonld.JsonLdService" filter="(osgi.jndi.service.name=jsonld)" />
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route id="JsonLdCompactHttp">
<from uri="jetty:http://0.0.0.0:{{rest.port}}/compact?matchOnUriPrefix=true&amp;httpMethodRestrict=GET&amp;sendServerVersion=false"/>
<to uri="direct:getResource"/>
<filter>
<simple>${header[CamelHttpResponseCode]} == 200</simple>
<log message="Converting resource ${headers[CamelFcrepoIdentifier]}"/>
<to uri="direct:compact"/>
</filter>
</route>
<route id="JsonLdResource">
<from uri="direct:getResource"/>
<removeHeaders pattern="breadcrumbId"/>
<removeHeaders pattern="Accept"/>
<removeHeaders pattern="User-Agent"/>
<setHeader headerName="CamelFcrepoIdentifier">
<simple>${headers[CamelHttpPath]}</simple>
</setHeader>
<setHeader headerName="CamelFcrepoBaseUrl">
<simple>{{fcrepo.baseUrl}}</simple>
</setHeader>
<to uri="fcrepo:{{fcrepo.baseUrl}}?accept=application/ld+json&amp;throwExceptionOnFailure=false"/>
</route>
<route id="JsonLdCompaction">
<from uri="direct:compact"/>
<setHeader headerName="JsonLdContext">
<simple>{{jsonld.context}}</simple>
</setHeader>
<setBody>
<method ref="jsonldService" method="compact(${body}, ${header[JsonLdContext]})"/>
</setBody>
</route>
</camelContext>
</blueprint>
#
# The logging properties used for testing
#
log4j.rootLogger=INFO, out
#log4j.logger.org.apache.camel=DEBUG
# CONSOLE appender not used by default
log4j.appender.out=org.apache.log4j.ConsoleAppender
log4j.appender.out.layout=org.apache.log4j.PatternLayout
log4j.appender.out.layout.ConversionPattern=[%30.30t] %-30.30c{1} %-5p %m%n
#log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
# File appender
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c{1} - %m %n
log4j.appender.file.file=target/camel-test.log
package edu.amherst.acdc.jsonld.cache;
import org.apache.camel.test.blueprint.CamelBlueprintTestSupport;
import org.junit.Test;
/**
* @author acoburn
* @since 9/21/15
*/
public class RouteTest extends CamelBlueprintTestSupport {
@Override
protected String getBlueprintDescriptor() {
return "/OSGI-INF/blueprint/blueprint.xml";
}
@Test
public void testRoute() throws Exception {
// the route is timer based, so every 5th second a message is send
// we should then expect at least one message
getMockEndpoint("mock:result").expectedMinimumMessageCount(0);
// assert expectations
assertMockEndpointsSatisfied();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%p %d{HH:mm:ss.SSS} \(%c{0}\) %m%n</pattern>
</encoder>
</appender>
<logger name="edu.amherst.acdc.jsonld.cache" additivity="false" level="INFO">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.apache.camel" additivity="false" level="INFO">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.fcrepo.camel" additivity="false" level="INFO">
<appender-ref ref="STDOUT"/>
</logger>
<root additivity="false" level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Repository JSON-LD handling service
===================================
This service transforms JSON-LD documents, either expanding or compacting them.
The service can be used with any camel route in an OSGi container.
Building
--------
To build this project use
mvn install
Deploying in OSGi
-----------------
Each of these projects can be deployed in an OSGi container. For example using
[Apache Karaf](http://karaf.apache.org) version 4.x or better, you can run the following
command from its shell:
feature:repo-add mvn:edu.amherst.acdc/acrepo-karaf/LATEST/xml/features
feature:install acrepo-jsonld-service
Or by copying any of the compiled bundles into `$KARAF_HOME/deploy`.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>repository-services</artifactId>
<groupId>edu.amherst.acdc</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>acrepo-jsonld-service</artifactId>
<packaging>bundle</packaging>
<name>JSON-LD expansion and compaction service bundle</name>
<description>Repository JSON-LD transformation service.</description>
<properties>
<osgi.export.packages>edu.amherst.acdc.jsonld;version=${project.version}</osgi.export.packages>
</properties>
<dependencies>
<dependency>
<groupId>com.github.jsonld-java</groupId>
<artifactId>jsonld-java</artifactId>
</dependency>
<!-- logging and testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
</dependencies>
<build>
<defaultGoal>install</defaultGoal>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.camel</groupId>
<artifactId>camel-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.amherst.acdc.jsonld;
import java.io.InputStream;
/**
* @author acoburn
* @since 9/14/15
*/
public interface JsonLdService {
/**
* Generate a compact representation of the input stream
*
* @param input The input JSON document
* @param contextUrl the location of a context URL
* @return the compacted JSON Object
*/
String compact(final InputStream input, final String contextUrl);
/**
* Generate a compact representation of the input stream
*
* @param input The input JSON document
* @param context the context document as an InputStream
* @return the compact JSON Object
*/
String compact(final InputStream input, final InputStream context);
/**
* Generate an expanded representation of the input document
*
* @param input the input JSON document
* @return the expanded JSON
*/
String expand(final InputStream input);
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.amherst.acdc.jsonld;
import static com.github.jsonldjava.utils.JsonUtils.fromInputStream;
import static com.github.jsonldjava.utils.JsonUtils.fromURL;
import static org.slf4j.LoggerFactory.getLogger;
import java.io.InputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.github.jsonldjava.core.JsonLdProcessor;
import com.github.jsonldjava.core.JsonLdOptions;
import com.github.jsonldjava.utils.JsonUtils;
import com.github.jsonldjava.core.JsonLdError;
import org.slf4j.Logger;
/**
* @author acoburn
* @since 9/21/15
*/
public class JsonLdServiceImpl implements JsonLdService {
private static final Logger LOGGER = getLogger(JsonLdServiceImpl.class);
final JsonLdOptions options;
/**
* Instantiate a JsonLdService object
*/
public JsonLdServiceImpl() {
options = new JsonLdOptions();
}
/**
* Generate a compact representation of the input stream
*
* @param input The input JSON document
* @param contextUrl the location of a context URL
* @return the compacted JSON Object
*/
public String compact(final InputStream input, final String contextUrl) {
LOGGER.info("using context from: {}", contextUrl);
try {
final List<Map<String, Object>> document = ((List<Map<String, Object>>)fromInputStream(input)).stream()
.filter(filterExport::test)
.collect(Collectors.toList());
return JsonUtils.toString(
JsonLdProcessor.compact(document, fromURL(new URL(contextUrl)), options));
} catch (final MalformedURLException ex) {
throw new RuntimeException("Invalid URL: " + ex.getMessage());
} catch (final JsonParseException ex) {
throw new RuntimeException("Error parsing JSON: " + ex.getMessage());
} catch (final JsonGenerationException ex) {
throw new RuntimeException("Error generating JSON: " + ex.getMessage());
} catch (final JsonLdError ex) {
throw new RuntimeException("Error converting JsonLd: " + ex.getMessage());
} catch (final IOException ex) {
throw new RuntimeException("Error reading/writing JSON document: " + ex.getMessage());
}
}
/**
* Generate a compact representation of the input stream
*
* @param input The input JSON document
* @param context the context document as an InputStream
* @return the compact JSON Object
*/
public String compact(final InputStream input, final InputStream context) {
try {
final List<Map<String, Object>> document = ((List<Map<String, Object>>)fromInputStream(input)).stream()
.filter(filterExport::test)
.collect(Collectors.toList());
return JsonUtils.toString(
JsonLdProcessor.compact(document, fromInputStream(context), options));
} catch (final JsonParseException ex) {
throw new RuntimeException("Error parsing JSON", ex);
} catch (final JsonGenerationException ex) {
throw new RuntimeException("Error generating JSON", ex);
} catch (final JsonLdError ex) {
throw new RuntimeException("Error converting JsonLd: " + ex.getMessage());
} catch (final IOException ex) {
throw new RuntimeException("Error reading/writing JSON document", ex);
}
}
/**