You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stanbol.apache.org by fl...@apache.org on 2012/06/19 00:51:28 UTC
svn commit: r1351524 [3/9] - in /incubator/stanbol/branches/cameltrial/flow:
jersey/src/main/java/org/apache/stanbol/flow/
jersey/src/main/java/org/apache/stanbol/flow/jersey/
jersey/src/main/java/org/apache/stanbol/flow/jersey/fragment/
jersey/src/mai...
Added: incubator/stanbol/branches/cameltrial/flow/jersey/src/main/java/org/apache/stanbol/flow/jersey/writers/ContentItemWriter.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/jersey/src/main/java/org/apache/stanbol/flow/jersey/writers/ContentItemWriter.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/jersey/src/main/java/org/apache/stanbol/flow/jersey/writers/ContentItemWriter.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/jersey/src/main/java/org/apache/stanbol/flow/jersey/writers/ContentItemWriter.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,545 @@
+/*
+ * 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 org.apache.stanbol.flow.jersey.writers;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM;
+import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
+import static javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA_TYPE;
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+import static javax.ws.rs.core.MediaType.WILDCARD_TYPE;
+import static org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper.getBlob;
+import static org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper.getContentParts;
+import static org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper.getMimeTypeWithParameters;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.ENHANCEMENT_PROPERTIES_URI;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.getEnhancementProperties;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.getOutputContent;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.getOutputContentParts;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.getParsedContentURIs;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.isOmitMetadata;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.isOmitParsedContent;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.clerezza.rdf.core.serializedform.Serializer;
+import org.apache.clerezza.rdf.core.serializedform.SupportedFormat;
+import org.apache.clerezza.rdf.jena.serializer.JenaSerializerProvider;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.entity.mime.FormBodyPart;
+import org.apache.http.entity.mime.HttpMultipart;
+import org.apache.http.entity.mime.MIME;
+import org.apache.http.entity.mime.content.AbstractContentBody;
+import org.apache.http.entity.mime.content.ContentBody;
+import org.apache.http.entity.mime.content.ContentDescriptor;
+import org.apache.http.entity.mime.content.StringBody;
+import org.apache.stanbol.commons.web.base.ContextHelper;
+import org.apache.stanbol.commons.web.base.writers.JsonLdSerializerProvider;
+import org.apache.stanbol.enhancer.servicesapi.Blob;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper;
+import org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+@Provider
+public class ContentItemWriter implements MessageBodyWriter<ContentItem> {
+
+ /**
+ * The "multipart/*" wilrcard
+ */
+ private static final MediaType MULTIPART = MediaType.valueOf(MULTIPART_FORM_DATA_TYPE.getType()+"/*");
+ private static final String CONTENT_ITEM_BOUNDARY = "contentItem";
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+ private static final MediaType DEFAULT_RDF_FORMAT = new MediaType(
+ APPLICATION_JSON_TYPE.getType(),
+ APPLICATION_JSON_TYPE.getSubtype(),
+ Collections.singletonMap("charset", UTF8.toString()));
+
+ private Serializer __serializer;
+
+ private ServletContext context;
+
+ public ContentItemWriter(@Context ServletContext context){
+ this.context = context;
+ }
+ /**
+ * Lazzy initialisation for the {@link Serializer}
+ * @return the {@link Serializer}
+ */
+ protected final Serializer getSerializer(){
+ /*
+ * Needed because Jersey tries to create an instance
+ * during initialisation. At that time the {@link BundleContext} required
+ * by {@link ContextHelper#getServiceFromContext(Class, ServletContext)}
+ * is not yet present resulting in an Exception.
+ */
+ if(__serializer == null){
+ if(context != null){
+ __serializer = ContextHelper.getServiceFromContext(Serializer.class, context);
+ } else {
+ __serializer = new Serializer();
+ __serializer.bindSerializingProvider(new JenaSerializerProvider());
+ __serializer.bindSerializingProvider(new JsonLdSerializerProvider());
+ }
+ }
+ return __serializer;
+ }
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ return //MediaType.MULTIPART_FORM_DATA_TYPE.isCompatible(mediaType) &&
+ ContentItem.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public long getSize(ContentItem t,
+ Class<?> type,
+ Type genericType,
+ Annotation[] annotations,
+ MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ public void writeTo(ContentItem ci,
+ Class<?> type,
+ Type genericType,
+ Annotation[] annotations,
+ MediaType mediaType,
+ MultivaluedMap<String,Object> httpHeaders,
+ OutputStream entityStream) throws IOException, WebApplicationException {
+
+ //(0) handle default dataType
+ Map<String,Object> properties = getEnhancementProperties(ci);
+ boolean omitMetadata = isOmitMetadata(properties);
+ if(!MULTIPART.isCompatible(mediaType)){ //two possible cases
+ if(!omitMetadata){ // (1) just return the RDF data
+ //(1.a) Backward support for default dataType if no Accept header is set
+ if (mediaType.isWildcardType() ||
+ TEXT_PLAIN_TYPE.isCompatible(mediaType) ||
+ APPLICATION_OCTET_STREAM_TYPE.isCompatible(mediaType)) {
+ mediaType = new MediaType(APPLICATION_JSON_TYPE.getType(),
+ APPLICATION_JSON_TYPE.getSubtype(),
+ //Clerezza serialisers are hard coded to use UTF-8
+ Collections.singletonMap("charset", UTF8.toString()));
+ httpHeaders.putSingle("Content-Type", mediaType.toString());
+ }
+ getSerializer().serialize(entityStream, ci.getMetadata(), mediaType.toString());
+ } else { // (2) return a single content part
+ Entry<UriRef,Blob> contentPart = getBlob(ci, Collections.singleton(mediaType.toString()));
+ if(contentPart == null){ //no alternate content with the requeste media type
+ throw new WebApplicationException(
+ Response.status(Response.Status.UNSUPPORTED_MEDIA_TYPE)
+ .entity("The requested enhancement chain has not created an " +
+ "version of the parsed content in the reuqest media " +
+ "type "+mediaType.toString())
+ .build());
+ } else { //found -> stream the content to the client
+ //NOTE: This assumes that the presence of a charset
+ // implies reading/writing character streams
+ String requestedCharset = mediaType.getParameters().get("charset");
+ String blobCharset = contentPart.getValue().getParameter().get("charset");
+ Charset readerCharset = blobCharset == null ? UTF8 : Charset.forName(blobCharset);
+ Charset writerCharset = requestedCharset == null ? null : Charset.forName(requestedCharset);
+ if(writerCharset != null && !writerCharset.equals(readerCharset)){
+ //we need to transcode
+ Reader reader = new InputStreamReader(
+ contentPart.getValue().getStream(),readerCharset);
+ Writer writer = new OutputStreamWriter(entityStream, writerCharset);
+ IOUtils.copy(reader, writer);
+ IOUtils.closeQuietly(reader);
+ } else { //no transcoding
+ if(requestedCharset == null && blobCharset != null){
+ httpHeaders.putSingle(HttpHeaders.CONTENT_TYPE,
+ mediaType.toString()+"; charset="+blobCharset);
+ }
+ InputStream in = contentPart.getValue().getStream();
+ IOUtils.copy(in, entityStream);
+ IOUtils.closeQuietly(in);
+ }
+ }
+ }
+ } else { // multipart mime requested!
+ final String charsetName = mediaType.getParameters().get("charset");
+ final Charset charset = charsetName != null ? Charset.forName(charsetName) : UTF8;
+ MediaType rdfFormat;
+ String rdfFormatString = EnhancementPropertiesHelper.getRdfFormat(properties);
+ if(rdfFormatString == null || rdfFormatString.isEmpty()){
+ rdfFormat = DEFAULT_RDF_FORMAT;
+ } else {
+ try {
+ rdfFormat = MediaType.valueOf(rdfFormatString);
+ if(rdfFormat.getParameters().get("charset") == null){
+ //use the charset of the default RDF format
+ rdfFormat = new MediaType(
+ rdfFormat.getType(), rdfFormat.getSubtype(),
+ DEFAULT_RDF_FORMAT.getParameters());
+ }
+ } catch (IllegalArgumentException e) {
+ throw new WebApplicationException(e,
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity(String.format("The specified RDF format '%s' (used "
+ + " to serialize all RDF parts of multipart MIME responses)"
+ + " is not a well formated MIME type",rdfFormatString))
+ .build());
+ }
+ }
+ //(1) setting the correct header
+ String contentType = String.format("%s/%s; charset=%s; boundary=%s",
+ mediaType.getType(),mediaType.getSubtype(),charset.toString(),CONTENT_ITEM_BOUNDARY);
+ httpHeaders.putSingle(HttpHeaders.CONTENT_TYPE,contentType);
+ HttpMultipart entity = new HttpMultipart("from-data", charset ,CONTENT_ITEM_BOUNDARY);
+ //(2) serialising the metadata
+ if(!isOmitMetadata(properties)){
+ entity.addBodyPart(new FormBodyPart("metadata", new ClerezzaContentBody(
+ ci.getUri().getUnicodeString(), ci.getMetadata(),
+ rdfFormat)));
+ }
+ //(3) serialising the Content (Bloby)
+ //(3.a) Filter based on parameter
+ List<Entry<UriRef,Blob>> includedBlobs = filterBlobs(ci, properties);
+ //(3.b) Serialise the filtered
+ if(!includedBlobs.isEmpty()) {
+ HttpMultipart content = new HttpMultipart("alternate", UTF8 ,"contentParts");
+ for(Entry<UriRef,Blob> entry : includedBlobs){
+ content.addBodyPart(new FormBodyPart(entry.getKey().getUnicodeString(),
+ new BlobContentBody(entry.getValue()))); //no file name
+ }
+ //add all the blobs
+ entity.addBodyPart(new FormBodyPart("content",new MultipartContentBody(content, null)));
+ } //else no content to include
+ Set<String> includeContentParts = getIncludedContentPartURIs(properties);
+ if(includeContentParts != null){
+ //(4) serialise EnhancementProperties
+ if(includeContentParts.isEmpty() || includeContentParts.contains(
+ ENHANCEMENT_PROPERTIES_URI.getUnicodeString())) {
+ JSONObject object;
+ try {
+ object = toJson(properties);
+ } catch (JSONException e) {
+ throw new WebApplicationException(e,
+ Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+ .entity("Unable to convert EnhancementProperties to " +
+ "JSON (values : "+properties+")!").build());
+ }
+ entity.addBodyPart(new FormBodyPart(
+ ENHANCEMENT_PROPERTIES_URI.getUnicodeString(),
+ new StringBody(object.toString(),MediaType.APPLICATION_JSON,UTF8)));
+ }
+ //(5) additional RDF metadata stored in contentParts
+ for(Entry<UriRef,TripleCollection> entry : getContentParts(ci, TripleCollection.class).entrySet()){
+ if(includeContentParts.isEmpty() || includeContentParts.contains(
+ entry.getKey())){
+ entity.addBodyPart(new FormBodyPart(entry.getKey().getUnicodeString(),
+ new ClerezzaContentBody(null, //no file name
+ entry.getValue(),rdfFormat)));
+ } // else ignore this content part
+ }
+ }
+ entity.writeTo(entityStream);
+ }
+
+ }
+ /**
+ * @param properties
+ * @return
+ */
+ private JSONObject toJson(Map<?,?> map) throws JSONException {
+ JSONObject object = new JSONObject();
+ for(Entry<?,?> entry : map.entrySet()){
+ Object value = getValue(entry.getValue());
+ object.put(entry.getKey().toString(),value);
+ }
+ return object;
+ }
+ /**
+ * @param entry
+ * @return
+ * @throws JSONException
+ */
+ private Object getValue(Object javaValue) throws JSONException {
+ Object value;
+ if(javaValue instanceof Collection<?>){
+ value = new JSONArray();
+ for(Object o : (Collection<?>)javaValue){
+ ((JSONArray)value).put(getValue(o));
+ }
+ } else if(javaValue instanceof Map<?,?>){
+ value = toJson((Map<?,?>)javaValue);
+ } else {
+ value = javaValue;
+ }
+ return value;
+ }
+ /**
+ * @param properties
+ * @return
+ */
+ private Set<String> getIncludedContentPartURIs(Map<String,Object> properties) {
+ Collection<String> ocp = getOutputContentParts(properties);
+ if(ocp == null || ocp.isEmpty()){
+ return null;
+ }
+ Set<String> includeContentParts = new HashSet<String>(ocp);
+ if(includeContentParts != null){
+ if(includeContentParts.isEmpty()){ //empty == none
+ includeContentParts = null;
+ } else if (includeContentParts.contains("*")){ // * == all -> empty list
+ includeContentParts = Collections.emptySet();
+ }
+ }
+ return includeContentParts;
+ }
+ /**
+ * @param ci
+ * @param properties
+ * @return
+ */
+ private List<Entry<UriRef,Blob>> filterBlobs(ContentItem ci, Map<String,Object> properties) {
+ final List<Entry<UriRef,Blob>> includedContentPartList;
+ Set<MediaType> includeMediaTypes = getIncludedMediaTypes(properties);
+ if(includeMediaTypes == null){
+ includedContentPartList = Collections.emptyList();
+ } else {
+ includedContentPartList = new ArrayList<Map.Entry<UriRef,Blob>>();
+ Set<String> ignoreContentPartUris = getIgnoredContentURIs(properties);
+ nextContentPartEntry:
+ for(Entry<UriRef,Blob> entry : getContentParts(ci,Blob.class).entrySet()){
+ if(!ignoreContentPartUris.contains(entry.getKey().getUnicodeString())){
+ Blob blob = entry.getValue();
+ MediaType blobMediaType = MediaType.valueOf(blob.getMimeType());
+ for(MediaType included : includeMediaTypes) {
+ if(blobMediaType.isCompatible(included)){
+ includedContentPartList.add(entry);
+ continue nextContentPartEntry;
+ }
+ }
+ } //else ignore this Blob
+ }
+ }
+ return includedContentPartList;
+ }
+ /**
+ * @param properties
+ * @return
+ */
+ private Set<String> getIgnoredContentURIs(Map<String,Object> properties) {
+ Set<String> ignoreContentPartUris = isOmitParsedContent(properties) ?
+ new HashSet<String>(getParsedContentURIs(properties)) : null;
+ if(ignoreContentPartUris == null){
+ ignoreContentPartUris = Collections.emptySet();
+ }
+ return ignoreContentPartUris;
+ }
+ /**
+ * @param properties
+ * @return
+ */
+ private Set<MediaType> getIncludedMediaTypes(Map<String,Object> properties) throws WebApplicationException {
+ Collection<String> includeMediaTypeStrings = getOutputContent(properties);
+ if(includeMediaTypeStrings == null){
+ return null;
+ }
+ Set<MediaType> includeMediaTypes = new HashSet<MediaType>(includeMediaTypeStrings.size());
+ for(String includeString : includeMediaTypeStrings){
+ if(includeString != null){
+ includeString.trim();
+ if(!includeString.isEmpty()){
+ if("*".equals(includeString)){ //also support '*' for '*/*'
+ includeMediaTypes.add(WILDCARD_TYPE);
+ } else {
+ try {
+ includeMediaTypes.add(MediaType.valueOf(includeString));
+ } catch (IllegalArgumentException e){
+ throw new WebApplicationException(e,
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity("The parsed outputContent parameter "
+ + includeMediaTypeStrings +" contain an "
+ + "illegal formated MediaType!")
+ .build());
+ }
+ }
+ }
+ }
+ }
+ if(includeMediaTypes.contains(WILDCARD_TYPE)){
+ includeMediaTypes = Collections.singleton(WILDCARD_TYPE);
+ }
+ return includeMediaTypes;
+ }
+
+ /**
+ * Supports sending multipart mime as {@link ContentBody}.
+ * @author Rupert Westenthaler
+ *
+ */
+ private class MultipartContentBody extends AbstractContentBody implements ContentBody,ContentDescriptor {
+
+ private HttpMultipart multipart;
+ private String name;
+
+ public MultipartContentBody(HttpMultipart multipart,String name){
+ super(String.format("multipart/%s; boundary=%s",
+ multipart.getSubType(), multipart.getBoundary()));
+ this.name = name;
+ this.multipart = multipart;
+ }
+ @Override
+ public String getCharset() {
+ return multipart.getCharset().toString();
+ }
+
+ @Override
+ public String getTransferEncoding() {
+ return MIME.ENC_8BIT;
+ }
+
+ @Override
+ public long getContentLength() {
+ return multipart.getTotalLength();
+ }
+
+ @Override
+ public String getFilename() {
+ return name;
+ }
+
+ @Override
+ public void writeTo(OutputStream out) throws IOException {
+ multipart.writeTo(out);
+ }
+
+ }
+ /**
+ * Supports serialised RDF graphs as {@link ContentBody}
+ * @author Rupert Westenthaler
+ *
+ */
+ private class ClerezzaContentBody extends AbstractContentBody implements ContentBody,ContentDescriptor {
+
+ private TripleCollection graph;
+ private String charset;
+ private String name;
+
+ protected ClerezzaContentBody(String name, TripleCollection graph, MediaType mimeType){
+ super(mimeType.getType()+'/'+mimeType.getSubtype());
+ charset = mimeType.getParameters().get("charset");
+ if(charset == null || charset.isEmpty()){
+ charset = UTF8.toString();
+ }
+ this.name = name;
+ this.graph = graph;
+ }
+
+ @Override
+ public String getCharset() {
+ return charset;
+ }
+
+ @Override
+ public String getTransferEncoding() {
+ return MIME.ENC_8BIT;
+ }
+
+ @Override
+ public long getContentLength() {
+ return -1;
+ }
+
+ @Override
+ public String getFilename() {
+ return name;
+ }
+
+ @Override
+ public void writeTo(OutputStream out) throws IOException {
+ getSerializer().serialize(out, graph, getMediaType()+'/'+getSubType());
+ }
+ }
+ private class BlobContentBody extends AbstractContentBody {
+
+ private Blob blob;
+
+ public BlobContentBody(Blob blob) {
+ super(blob.getMimeType());
+ this.blob = blob;
+ }
+
+ @Override
+ public String getFilename() {
+ return null;
+ }
+
+ @Override
+ public void writeTo(OutputStream out) throws IOException {
+ InputStream in = blob.getStream();
+ IOUtils.copy(in, out);
+ IOUtils.closeQuietly(in);
+ }
+
+ @Override
+ public String getCharset() {
+ return blob.getParameter().get("charset");
+ }
+
+ @Override
+ public String getTransferEncoding() {
+ return blob.getParameter().get("charset") == null ?
+ MIME.ENC_BINARY : MIME.ENC_8BIT;
+ }
+
+ @Override
+ public long getContentLength() {
+ return -1;
+ }
+
+ }
+
+}
Propchange: incubator/stanbol/branches/cameltrial/flow/servicesapi/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Jun 18 22:51:22 2012
@@ -0,0 +1,11 @@
+target
+bin
+*.iml
+*.ipr
+*.iws
+.settings
+.project
+.classpath
+.externalToolBuilders
+maven-eclipse.xml
+.metadata
Added: incubator/stanbol/branches/cameltrial/flow/servicesapi/pom.xml
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/servicesapi/pom.xml?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/servicesapi/pom.xml (added)
+++ incubator/stanbol/branches/cameltrial/flow/servicesapi/pom.xml Mon Jun 18 22:51:22 2012
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<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>
+ <groupId>org.apache.stanbol</groupId>
+ <artifactId>org.apache.stanbol.flow.reactor</artifactId>
+ <version>0.9.0-incubating-SNAPSHOT</version>
+ <relativePath>../</relativePath>
+ </parent>
+
+ <artifactId>org.apache.stanbol.flow.servicesapi</artifactId>
+ <packaging>bundle</packaging>
+
+ <name>Apache Stanbol Flow : Services API</name>
+ <description>The Stanbol Flow Services API</description>
+
+ <inceptionYear>2012</inceptionYear>
+
+ <scm>
+ <connection>
+ scm:svn:http://svn.apache.org/repos/asf/incubator/stanbol/trunk/flow/servicesapi/
+ </connection>
+ <developerConnection>
+ scm:svn:https://svn.apache.org/repos/asf/incubator/stanbol/trunk/flow/servicesapi/
+ </developerConnection>
+ <url>http://incubator.apache.org/stanbol/</url>
+ </scm>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Export-Package>
+ org.apache.stanbol.enhancer.servicesapi;version=${project.version},
+ org.apache.stanbol.enhancer.servicesapi.helper;version=${project.version},
+ org.apache.stanbol.enhancer.servicesapi.rdf;version=${project.version}
+ </Export-Package>
+ <Private-Package>
+ org.apache.stanbol.enhancer.servicesapi.helper.impl;version=${project.version}
+ </Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.stanbol</groupId>
+ <artifactId>org.apache.stanbol.enhancer.servicesapi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.clerezza</groupId>
+ <artifactId>rdf.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+ <!-- for tests -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
Added: incubator/stanbol/branches/cameltrial/flow/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/FlowJobManager.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/FlowJobManager.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/FlowJobManager.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/servicesapi/src/main/java/org/apache/stanbol/enhancer/servicesapi/FlowJobManager.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,72 @@
+/*
+* 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 org.apache.stanbol.enhancer.servicesapi;
+
+import java.util.List;
+
+/**
+ * Accept requests for enhancing ContentItems, and processes them either
+ * synchronously or asynchronously (as decided by the enhancement engines or by
+ * configuration).
+ * <p>
+ * The progress of the enhancement process should be made accessible in the
+ * ContentItem's metadata.
+ */
+public interface FlowJobManager {
+
+ /**
+ * Default chain name to use in jobmanager implementation and wanted default chain
+ */
+ String DEFAULT_CHAIN_NAME = "defaultChain";
+
+ /**
+ * Enhances the parsed contentItem by using the default enhancement
+ * Chain.
+ * Create relevant asynchronous requests or enhance content immediately. The
+ * result is not persisted right now. The caller is responsible for calling the
+ * {@link Store#put(ContentItem)} afterwards in case persistence is
+ * required.
+ * <p>
+ * TODO: define the expected semantics if asynchronous enhancements were to
+ * get implemented.
+ * @throws EnhancementException if the enhancement process failed
+ */
+ void enhanceContent(ContentItem ci) throws EnhancementException;
+
+ /**
+ * Processes the parsed {@link ContentItem} by using the
+ * {@link Chain#getExecutionPlan() execution plan} provided by the
+ * {@link Chain}.
+ * @param ci : ContentItem to be enhanced
+ * @param chain : The enhancement Chain used to process the content item
+ * @throws EnhancementException : if an Engine required by the Chain fails to
+ * process the ContentItem
+ * @throws ChainException : if the enhancement process failed
+ */
+ void enhanceContent(ContentItem ci, Chain chain) throws EnhancementException;
+
+ /**
+ * Return the unmodifiable list of active registered engine instance that
+ * can be used by the manager.
+ * @deprecated use the {@link EnhancementEngineManager} to get information
+ * about currently active Engines and the {@link ChainManager} to get active
+ * chains. This method will now return active engines of the default chain.
+ */
+ @Deprecated
+ List<EnhancementEngine> getActiveEngines();
+
+}
Added: incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/fragment/EnhancerWebFragment.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/fragment/EnhancerWebFragment.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/fragment/EnhancerWebFragment.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/fragment/EnhancerWebFragment.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,139 @@
+/*
+* 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 org.apache.stanbol.flow.jersey.fragment;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.clerezza.rdf.core.access.TcManager;
+import org.apache.clerezza.rdf.core.serializedform.Serializer;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.stanbol.commons.web.base.LinkResource;
+import org.apache.stanbol.commons.web.base.NavigationLink;
+import org.apache.stanbol.commons.web.base.ScriptResource;
+import org.apache.stanbol.commons.web.base.WebFragment;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementJobManager;
+import org.apache.stanbol.flow.jersey.reader.ContentItemReader;
+import org.apache.stanbol.flow.jersey.resource.FlowRootResource;
+import org.apache.stanbol.flow.jersey.writers.ContentItemWriter;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+
+import freemarker.cache.ClassTemplateLoader;
+import freemarker.cache.TemplateLoader;
+
+/**
+ * Statically define the list of available resources and providers to be contributed to the the Stanbol JAX-RS
+ * Endpoint.
+ */
+@Component(immediate = true, metatype = true)
+@Service
+public class EnhancerWebFragment implements WebFragment {
+
+ private static final String NAME = "flow";
+
+ private static final String STATIC_RESOURCE_PATH = "/org/apache/stanbol/flow/jersey/static";
+
+ private static final String TEMPLATE_PATH = "/org/apache/stanbol/flow/jersey/templates";
+
+ private BundleContext bundleContext;
+
+ @Reference
+ EnhancementJobManager jobManager;
+
+ @Reference
+ TcManager tcManager;
+
+ @Reference
+ Serializer serializer;
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Activate
+ protected void activate(ComponentContext ctx) {
+ this.bundleContext = ctx.getBundleContext();
+ }
+
+ @Override
+ public Set<Class<?>> getJaxrsResourceClasses() {
+ Set<Class<?>> classes = new HashSet<Class<?>>();
+ // resources
+ //classes.add(EnginesRootResource.class);
+ classes.add(FlowRootResource.class);
+ //classes.add(EnhancementChainResource.class);
+ //classes.add(ChainsRootResource.class);
+ //classes.add(EnhancementEnginesRootResource.class);
+ //classes.add(EnhancementEngineResource.class);
+ //Reader/Writer for ContentItems
+ classes.add(ContentItemReader.class);
+ classes.add(ContentItemWriter.class);
+ return classes;
+ }
+
+ @Override
+ public Set<Object> getJaxrsResourceSingletons() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public String getStaticResourceClassPath() {
+ return STATIC_RESOURCE_PATH;
+ }
+
+ @Override
+ public TemplateLoader getTemplateLoader() {
+ return new ClassTemplateLoader(getClass(), TEMPLATE_PATH);
+ }
+
+ @Override
+ public List<LinkResource> getLinkResources() {
+ List<LinkResource> resources = new ArrayList<LinkResource>();
+ resources.add(new LinkResource("stylesheet", "openlayers-2.9/theme/default/style.css", this, 10));
+ resources.add(new LinkResource("stylesheet", "scripts/prettify/prettify.css", this, 20));
+ return resources;
+ }
+
+ @Override
+ public List<ScriptResource> getScriptResources() {
+ List<ScriptResource> resources = new ArrayList<ScriptResource>();
+ resources.add(new ScriptResource("text/javascript", "openlayers-2.9/OpenLayers.js", this, 10));
+ resources.add(new ScriptResource("text/javascript", "scripts/prettify/prettify.js", this, 20));
+ return resources;
+ }
+
+ @Override
+ public List<NavigationLink> getNavigationLinks() {
+ List<NavigationLink> links = new ArrayList<NavigationLink>();
+ links.add(new NavigationLink("flow", "/flow", "/imports/enginesDescription.ftl", 10));
+ return links;
+ }
+
+ @Override
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+}
Added: incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/reader/ContentItemReader.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/reader/ContentItemReader.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/reader/ContentItemReader.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/reader/ContentItemReader.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,389 @@
+/*
+ * 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 org.apache.stanbol.flow.jersey.reader;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+import static org.apache.stanbol.enhancer.servicesapi.helper.EnhancementEngineHelper.randomUUID;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.ENHANCEMENT_PROPERTIES_URI;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.PARSED_CONTENT_URIS;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.getEnhancementProperties;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.clerezza.rdf.core.MGraph;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.clerezza.rdf.core.serializedform.Parser;
+import org.apache.clerezza.rdf.jena.parser.JenaParserProvider;
+import org.apache.commons.fileupload.FileItemIterator;
+import org.apache.commons.fileupload.FileItemStream;
+import org.apache.commons.fileupload.FileUpload;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.RequestContext;
+import org.apache.commons.io.IOUtils;
+import org.apache.stanbol.commons.indexedgraph.IndexedMGraph;
+import org.apache.stanbol.commons.web.base.ContextHelper;
+import org.apache.stanbol.enhancer.servicesapi.Blob;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryBlob;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryContentItem;
+import org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.codehaus.jettison.mapped.SimpleConverter;
+import org.mortbay.log.Log;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Provider
+public class ContentItemReader implements MessageBodyReader<ContentItem> {
+
+ private static Logger log = LoggerFactory.getLogger(ContentItemReader.class);
+ FileUpload fu = new FileUpload();
+ private Parser __parser;
+ private ServletContext context;
+
+ public static final MediaType MULTIPART = MediaType.valueOf(MediaType.MULTIPART_FORM_DATA_TYPE.getType()+"/*");
+
+ public ContentItemReader(@Context ServletContext context) {
+ this.context = context;
+ }
+ /**
+ * Lazy initialisation for the parser.
+ * @return teh parser
+ */
+ protected final Parser getParser(){
+ /*
+ * Needed because Jersey tries to create an instance
+ * during initialisation. At that time the {@link BundleContext} required
+ * by {@link ContextHelper#getServiceFromContext(Class, ServletContext)}
+ * is not yet present resulting in an Exception.
+ */
+ if(__parser == null){
+ if(context != null){
+ __parser = ContextHelper.getServiceFromContext(Parser.class, context);
+ } else { //mainly for unit tests we want also allow initialisation without context
+ __parser = new Parser();
+ __parser.bindParsingProvider(new JenaParserProvider());
+ }
+ }
+ return __parser;
+ }
+
+ @Override
+ public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ return ContentItem.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public ContentItem readFrom(Class<ContentItem> type,
+ Type genericType,
+ Annotation[] annotations,
+ MediaType mediaType,
+ MultivaluedMap<String,String> httpHeaders,
+ InputStream entityStream) throws IOException, WebApplicationException {
+ //boolean withMetadata = withMetadata(httpHeaders);
+ ContentItem contentItem = null;
+ Set<String> parsedContentIds = new HashSet<String>();
+ if(mediaType.isCompatible(MULTIPART)){
+ //try to read ContentItem from "multipart/from-data"
+ MGraph metadata = null;
+ FileItemIterator fileItemIterator;
+ String contentItemId = null;
+ try {
+ fileItemIterator = fu.getItemIterator(new MessageBodyReaderContext(entityStream, mediaType));
+ while(fileItemIterator.hasNext()){
+ FileItemStream fis = fileItemIterator.next();
+ if(fis.getFieldName().equals("metadata")){
+ if(contentItem != null){
+ throw new WebApplicationException(
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity("The Multipart MIME part with the 'metadata' " +
+ "MUST BE before the MIME part containing the " +
+ "'content'!").build());
+ }
+ //the metadata may define the ID for the contentItem
+ if(fis.getName() != null && !fis.getName().isEmpty()){
+ contentItemId = fis.getName();
+ }
+ metadata = new IndexedMGraph();
+ try {
+ getParser().parse(metadata, fis.openStream(), fis.getContentType());
+ } catch (Exception e) {
+ throw new WebApplicationException(e,
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity(String.format("Unable to parse Metadata " +
+ "from Multipart MIME part '%s' (" +
+ "contentItem: %s| contentType: %s)",
+ fis.getFieldName(),fis.getName(),fis.getContentType()))
+ .build());
+ }
+ } else if(fis.getFieldName().equals("content")){
+ contentItem = createContentItem(contentItemId, metadata, fis, parsedContentIds);
+ } else if(fis.getFieldName().equals("properties") ||
+ fis.getFieldName().equals(ENHANCEMENT_PROPERTIES_URI.getUnicodeString())){
+ //parse the enhancementProperties
+ if(contentItem == null){
+ throw new WebApplicationException(
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity("Multipart MIME parts for " +
+ "EnhancementProperties MUST BE after the " +
+ "MIME parts for 'metadata' AND 'content'")
+ .build());
+ }
+ MediaType propMediaType = MediaType.valueOf(fis.getContentType());
+ if(!APPLICATION_JSON_TYPE.isCompatible(propMediaType)){
+ throw new WebApplicationException(
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity("EnhancementProperties (Multipart MIME parts" +
+ "with the name '"+fis.getFieldName()+"') MUST " +
+ "BE encoded as 'appicaltion/json' (encountered: '" +
+ fis.getContentType()+"')!")
+ .build());
+ }
+ String propCharset = propMediaType.getParameters().get("charset");
+ if(propCharset == null){
+ propCharset = "UTF-8";
+ }
+ Map<String,Object> enhancementProperties = getEnhancementProperties(contentItem);
+ try {
+ enhancementProperties.putAll(toMap(new JSONObject(
+ IOUtils.toString(fis.openStream(),propCharset))));
+ } catch (JSONException e) {
+ throw new WebApplicationException(e,
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity("Unable to parse EnhancementProperties from" +
+ "Multipart MIME parts with the name 'properties'!")
+ .build());
+ }
+
+ } else { //additional metadata as serialised RDF
+ if(contentItem == null){
+ throw new WebApplicationException(
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity("Multipart MIME parts for additional " +
+ "contentParts MUST BE after the MIME " +
+ "parts for 'metadata' AND 'content'")
+ .build());
+ }
+ if(fis.getFieldName() == null || fis.getFieldName().isEmpty()){
+ throw new WebApplicationException(
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity("Multipart MIME parts representing " +
+ "ContentParts for additional RDF metadata" +
+ "MUST define the contentParts URI as" +
+ "'name' of the MIME part!").build());
+ }
+ MGraph graph = new IndexedMGraph();
+ try {
+ getParser().parse(graph, fis.openStream(), fis.getContentType());
+ } catch (Exception e) {
+ throw new WebApplicationException(e,
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity(String.format("Unable to parse RDF " +
+ "for ContentPart '%s' ( contentType: %s)",
+ fis.getName(),fis.getContentType()))
+ .build());
+ }
+ UriRef contentPartId = new UriRef(fis.getFieldName());
+ contentItem.addPart(contentPartId, graph);
+ }
+ }
+ if(contentItem == null){
+ throw new WebApplicationException(
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity("The parsed multipart content item does not contain "
+ + "any content. The content is expected to be contained "
+ + "in a MIME part with the name 'content'. This part can "
+ + " be also a 'multipart/alternate' if multiple content "
+ + "parts need to be included in requests.").build());
+ }
+ } catch (FileUploadException e) {
+ throw new WebApplicationException(e, Response.Status.BAD_REQUEST);
+ }
+ } else { //normal content
+ contentItem = new InMemoryContentItem(
+ IOUtils.toByteArray(entityStream), mediaType.toString());
+ //add the URI of the main content
+ parsedContentIds.add(contentItem.getPartUri(0).getUnicodeString());
+ }
+ //set the parsed contentIDs to the EnhancementProperties
+ getEnhancementProperties(contentItem).put(PARSED_CONTENT_URIS,
+ Collections.unmodifiableSet(parsedContentIds));
+ return contentItem;
+ }
+ /**
+ * Creates a ContentItem
+ * @param id the ID or <code>null</code> if not known
+ * @param metadata the metadata or <code>null</code> if not parsed. NOTE that
+ * if <code>id == null</code> also <code>metadata == null</code> and
+ * <code>id != null</code> also <code>metadata != null</code>.
+ * @param content the {@link FileItemStream} of the MIME part representing
+ * the content. If {@link FileItemStream#getContentType()} is compatible with
+ * "multipart/*" than this will further parse for multiple parsed content
+ * version. In any other case the contents of the parsed {@link FileItemStream}
+ * will be directly add as content for the {@link ContentItem} created by
+ * this method.
+ * @param parsedContentParts used to add the IDs of parsed contentParts
+ * @return the created content item
+ * @throws IOException on any error while accessing the contents of the parsed
+ * {@link FileItemStream}
+ * @throws FileUploadException if the parsed contents are not correctly
+ * encoded Multipoart MIME
+ */
+ private ContentItem createContentItem(String id, MGraph metadata, FileItemStream content,Set<String> parsedContentParts) throws IOException, FileUploadException {
+ MediaType partContentType = MediaType.valueOf(content.getContentType());
+ ContentItem contentItem = null;
+ if(MULTIPART.isCompatible(partContentType)){
+ //multiple contentParts are parsed
+ FileItemIterator contentPartIterator = fu.getItemIterator(
+ new MessageBodyReaderContext(
+ content.openStream(), partContentType));
+ while(contentPartIterator.hasNext()){
+ FileItemStream fis = contentPartIterator.next();
+ if(contentItem == null){
+ log.debug("create ContentItem {} for content (type:{})",
+ id,content.getContentType());
+ contentItem = new InMemoryContentItem(id,
+ IOUtils.toByteArray(fis.openStream()),
+ fis.getContentType(), metadata);
+ } else {
+ Blob blob = new InMemoryBlob(fis.openStream(), fis.getContentType());
+ UriRef contentPartId = null;
+ if(fis.getFieldName() != null && !fis.getFieldName().isEmpty()){
+ contentPartId = new UriRef(fis.getFieldName());
+ } else {
+ //generating a random ID might break metadata
+ //TODO maybe we should throw an exception instead
+ contentPartId = new UriRef("urn:contentpart:"+ randomUUID());
+ }
+ log.debug(" ... add Blob {} to ContentItem {} with content (type:{})",
+ new Object[]{contentPartId, id, fis.getContentType()});
+ contentItem.addPart(contentPartId, blob);
+ parsedContentParts.add(contentPartId.getUnicodeString());
+ }
+ }
+ } else {
+ log.debug("create ContentItem {} for content (type:{})",
+ id,content.getContentType());
+ contentItem = new InMemoryContentItem(id,
+ IOUtils.toByteArray(content.openStream()),
+ content.getContentType(), metadata);
+ }
+ //add the URI of the main content to the parsed contentParts
+ parsedContentParts.add(contentItem.getPartUri(0).getUnicodeString());
+ return contentItem;
+ }
+
+ /**
+ * Adapter from the parameter present in an {@link MessageBodyReader} to
+ * the {@link RequestContext} as used by the commons.fileupload framework
+ * @author rwesten
+ *
+ */
+ private static class MessageBodyReaderContext implements RequestContext{
+
+ private final InputStream in;
+ private final String contentType;
+ private final String charEncoding;
+
+ public MessageBodyReaderContext(InputStream in, MediaType mediaType){
+ this.in = in;
+ this.contentType = mediaType.toString();
+ String charset = mediaType.getParameters().get("charset");
+ this.charEncoding = charset == null ? "UTF-8" : charset;
+ }
+
+ @Override
+ public String getCharacterEncoding() {
+ return charEncoding;
+ }
+
+ @Override
+ public String getContentType() {
+ return contentType;
+ }
+
+ @Override
+ public int getContentLength() {
+ return -1;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return in;
+ }
+
+ }
+ /**
+ * Converts a JSON object to a java Map. Nested JSONArrays are converted
+ * to collections and nested JSONObjects are converted to Maps.
+ * @param object
+ * @return
+ * @throws JSONException
+ */
+ private Map<String,Object> toMap(JSONObject object) throws JSONException {
+ Map<String,Object> data = new HashMap<String,Object>();
+ for(Iterator<?> keys = object.keys();keys.hasNext();){
+ String key = (String)keys.next();
+ data.put(key, getValue(object.get(key)));
+ }
+
+ return data;
+ }
+ /**
+ * @param object
+ * @param data
+ * @param key
+ * @throws JSONException
+ */
+ private Object getValue(Object value) throws JSONException {
+ if(value instanceof JSONObject){
+ return toMap((JSONObject)value);
+ } else if(value instanceof JSONArray){
+ Collection<Object> values = new ArrayList<Object>(((JSONArray)value).length());
+ for(int i=0;i<((JSONArray)value).length();i++){
+ values.add(getValue(((JSONArray)value).get(i)));
+ }
+ return values;
+ } else {
+ return value;
+ }
+ }
+
+}
Added: incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/AbstractEnhancerResource.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/AbstractEnhancerResource.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/AbstractEnhancerResource.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/AbstractEnhancerResource.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,228 @@
+/*
+ * 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 org.apache.stanbol.flow.jersey.resource;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.WILDCARD;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.N3;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.N_TRIPLE;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.RDF_JSON;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.RDF_XML;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.TURTLE;
+import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.X_TURTLE;
+import static org.apache.stanbol.commons.web.base.CorsHelper.addCORSOrigin;
+import static org.apache.stanbol.commons.web.base.CorsHelper.enableCORS;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.INCLUDE_EXECUTION_METADATA;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.OMIT_METADATA;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.OMIT_PARSED_CONTENT;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.OUTPUT_CONTENT;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.OUTPUT_CONTENT_PART;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.RDF_FORMAT;
+import static org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper.getEnhancementProperties;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+import org.apache.clerezza.rdf.core.MGraph;
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.stanbol.commons.web.base.ContextHelper;
+import org.apache.stanbol.commons.web.base.resource.BaseStanbolResource;
+import org.apache.stanbol.commons.web.base.utils.MediaTypeUtil;
+import org.apache.stanbol.enhancer.servicesapi.Chain;
+import org.apache.stanbol.enhancer.servicesapi.ChainException;
+import org.apache.stanbol.enhancer.servicesapi.ChainManager;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.EngineException;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementEngineManager;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementException;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementJobManager;
+import org.apache.stanbol.enhancer.servicesapi.NoSuchPartException;
+import org.apache.stanbol.enhancer.servicesapi.rdf.ExecutionMetadata;
+import org.apache.stanbol.flow.jersey.utils.EnhancementPropertiesHelper;
+import org.apache.stanbol.enhancer.servicesapi.FlowJobManager;
+
+/**
+ * Abstract super class for all enhancement endpoints that do not use/support
+ * the default Enhancer Web UI.<p>
+ * This is mainly used for supporting enhancement requests to single
+ * enhancement engines.
+ *
+ * @author Rupert Westenthaler
+ *
+ */
+public abstract class AbstractEnhancerResource extends BaseStanbolResource {
+
+ //protected final EnhancementJobManager jobManager;
+ protected final FlowJobManager jobManager;
+ protected final EnhancementEngineManager engineManager;
+ protected final ChainManager chainManager;
+
+ public AbstractEnhancerResource(@Context ServletContext context) {
+ super();
+ // bind the job manager by looking it up from the servlet request context
+ //jobManager = ContextHelper.getServiceFromContext(EnhancementJobManager.class, context);
+ jobManager = ContextHelper.getServiceFromContext(FlowJobManager.class, context);
+ chainManager = ContextHelper.getServiceFromContext(ChainManager.class, context);
+ engineManager = ContextHelper.getServiceFromContext(EnhancementEngineManager.class, context);
+ }
+ /**
+ * Getter for the Enhancement {@link Chain}
+ * @return the enhancement chain. MUST NOT return <code>null</code>
+ * @throws ChainException if the Chain is currently not available
+ */
+ protected abstract Chain getChain() throws ChainException;
+
+ @OPTIONS
+ public Response handleCorsPreflight(@Context HttpHeaders headers) {
+ ResponseBuilder res = Response.ok();
+ enableCORS(servletContext, res, headers);
+ return res.build();
+ }
+
+ @OPTIONS
+ @Path("/ep")
+ public Response handleEpCorsPreflight(@Context HttpHeaders headers) {
+ ResponseBuilder res = Response.ok();
+ enableCORS(servletContext, res, headers,HttpMethod.OPTIONS,HttpMethod.GET);
+ return res.build();
+ }
+
+ @GET
+ @Path("/ep")
+ @Produces(value = {APPLICATION_JSON, N3, N_TRIPLE, RDF_JSON, RDF_XML, TURTLE, X_TURTLE})
+ public Response getExecutionPlan(@Context HttpHeaders headers) {
+ ResponseBuilder res;
+ Chain chain = null;
+ try {
+ chain = getChain();
+ res = Response.ok(chain.getExecutionPlan());
+ } catch (ChainException e) {
+ String chainName = chain == null ? "" : ("'"+chain.getName()+"' ");
+ res = Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+ .entity("The Enhancement Chain "+chainName+"is currently" +
+ "not executeable (message: "+e.getMessage()+")!");
+ }
+ addCORSOrigin(servletContext, res, headers);
+ return res.build();
+
+
+ }
+
+ /**
+ * Media-Type based handling of the raw POST data.
+ *
+ * @param data
+ * binary payload to analyze
+ * @param uri
+ * optional URI for the content items (to be used as an identifier in the enhancement graph)
+ * @throws EngineException
+ * if the content is somehow corrupted
+ * @throws IOException
+ */
+ @POST
+ @Consumes(WILDCARD)
+ public Response enhanceFromData(ContentItem ci,
+ @QueryParam(value = "uri") String uri,
+ @QueryParam(value = "executionmetadata") boolean inclExecMetadata,
+ @QueryParam(value = "outputContent") Set<String> mediaTypes,
+ @QueryParam(value = "omitParsed") boolean omitParsed,
+ @QueryParam(value = "outputContentPart") Set<String> contentParts,
+ @QueryParam(value = "omitMetadata") boolean omitMetadata,
+ @QueryParam(value = "rdfFormat") String rdfFormat,
+ @Context HttpHeaders headers) throws EnhancementException, IOException {
+ Map<String,Object> enhancementProperties = getEnhancementProperties(ci);
+ enhancementProperties.put(INCLUDE_EXECUTION_METADATA, inclExecMetadata);
+ if(mediaTypes != null && !mediaTypes.isEmpty()){
+ enhancementProperties.put(OUTPUT_CONTENT, mediaTypes);
+ }
+ enhancementProperties.put(OMIT_PARSED_CONTENT, omitParsed);
+ if(contentParts != null && !contentParts.isEmpty()){
+ Set<UriRef> outputContentParts = new HashSet<UriRef>();
+ for(String contentPartUri : contentParts){
+ if(contentPartUri != null && !contentPartUri.isEmpty()){
+ if("*".equals(contentPartUri)){
+ outputContentParts.add(null); //indicated wildcard
+ } else {
+ outputContentParts.add(new UriRef(contentPartUri));
+ }
+ }
+ }
+ enhancementProperties.put(OUTPUT_CONTENT_PART, outputContentParts);
+ }
+ enhancementProperties.put(OMIT_METADATA, omitMetadata);
+ if(rdfFormat != null && !rdfFormat.isEmpty()){
+ try {
+ enhancementProperties.put(RDF_FORMAT,MediaType.valueOf(rdfFormat).toString());
+ } catch (IllegalArgumentException e) {
+ throw new WebApplicationException(e,
+ Response.status(Response.Status.BAD_REQUEST)
+ .entity(String.format("Unable to parse MediaType form parameter" +
+ "rdfFormat=%s",rdfFormat))
+ .build());
+ }
+ }
+ enhance(ci);
+ ResponseBuilder rb = Response.ok(ci);
+ MediaType mediaType = MediaTypeUtil.getAcceptableMediaType(headers, null);
+ if (mediaType != null) {
+ rb.header(HttpHeaders.CONTENT_TYPE, mediaType);
+ }
+ addCORSOrigin(servletContext, rb, headers);
+ return rb.build();
+ }
+
+ /**
+ * Enhances the parsed ContentItem
+ * @param ci the content item to enhance
+ * @throws EnhancementException
+ */
+ protected void enhance(ContentItem ci) throws EnhancementException {
+ Map<String,Object> enhancementPropertis = EnhancementPropertiesHelper.getEnhancementProperties(ci);
+ if (jobManager != null) {
+ jobManager.enhanceContent(ci, getChain());
+ }
+ MGraph graph = ci.getMetadata();
+ Boolean includeExecutionMetadata = (Boolean)enhancementPropertis.get(INCLUDE_EXECUTION_METADATA);
+ if (includeExecutionMetadata != null && includeExecutionMetadata.booleanValue()) {
+ try {
+ graph.addAll(ci.getPart(ExecutionMetadata.CHAIN_EXECUTION, TripleCollection.class));
+ } catch (NoSuchPartException e) {
+ // no executionMetadata available
+ }
+ }
+ }
+
+}
\ No newline at end of file
Added: incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/AbstractEnhancerUiResource.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/AbstractEnhancerUiResource.java?rev=1351524&view=auto
==============================================================================
--- incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/AbstractEnhancerUiResource.java (added)
+++ incubator/stanbol/branches/cameltrial/flow/web/src/main/java/org/apache/stanbol/flow/jersey/resource/AbstractEnhancerUiResource.java Mon Jun 18 22:51:22 2012
@@ -0,0 +1,242 @@
+/*
+ * 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 org.apache.stanbol.flow.jersey.resource;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
+import static javax.ws.rs.core.MediaType.TEXT_HTML;
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
+import static org.apache.stanbol.commons.web.base.CorsHelper.addCORSOrigin;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+import org.apache.clerezza.rdf.core.Graph;
+import org.apache.clerezza.rdf.core.NonLiteral;
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.access.TcManager;
+import org.apache.clerezza.rdf.core.serializedform.Serializer;
+import org.apache.stanbol.commons.web.base.ContextHelper;
+import org.apache.stanbol.enhancer.servicesapi.Chain;
+import org.apache.stanbol.enhancer.servicesapi.ChainException;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.EngineException;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementEngine;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementException;
+import org.apache.stanbol.enhancer.servicesapi.helper.ExecutionPlanHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.InMemoryContentItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.jersey.api.view.Viewable;
+
+/**
+ * Abstract super class for all Enhancer endpoint that do also provide the
+ * Stanbol Enhancer Web UI. This includes "/enhancer", /enhancer/chain/{name}
+ * and "/engines".
+ *
+ * @author Rupert Westenthaler
+ *
+ */
+public abstract class AbstractEnhancerUiResource extends AbstractEnhancerResource {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ protected final TcManager tcManager;
+ protected final Serializer serializer;
+ private LinkedHashSet<ExecutionNode> _executionNodes;
+ private LinkedHashSet<ExecutionNode> _activeNodes;
+ protected final Chain chain;
+
+ public AbstractEnhancerUiResource(String chainName,ServletContext context) {
+ super(context);
+ serializer = ContextHelper.getServiceFromContext(Serializer.class, context);
+ tcManager = ContextHelper.getServiceFromContext(TcManager.class, context);
+ if(chainName == null){
+ chain = chainManager.getDefault();
+ } else {
+ this.chain = chainManager.getChain(chainName);
+ }
+ if(this.chain == null){
+ throw new WebApplicationException(Response.Status.NOT_FOUND);
+ }
+ }
+
+ @GET
+ @Produces(TEXT_HTML)
+ public Response get(@Context HttpHeaders headers) {
+ ResponseBuilder res = Response.ok(new Viewable("index", this), TEXT_HTML);
+ addCORSOrigin(servletContext, res, headers);
+ return res.build();
+ }
+
+ /**
+ * Form-based OpenCalais-compatible interface
+ *
+ * TODO: should we parse the OpenCalais paramsXML and find the closest Stanbol Enhancer semantics too?
+ *
+ * Note: the format parameter is not part of the official API
+ *
+ * @throws EngineException
+ * if the content is somehow corrupted
+ * @throws IOException
+ */
+ @POST
+ @Consumes(APPLICATION_FORM_URLENCODED)
+ public Response enhanceFromForm(@FormParam("content") String content, @FormParam("format") String format, @FormParam("ajax") boolean buildAjaxview, @Context HttpHeaders headers) throws EnhancementException,
+ IOException {
+ log.info("enhance from From: " + content);
+ ContentItem ci = new InMemoryContentItem(content.getBytes("UTF-8"), TEXT_PLAIN);
+ if(!buildAjaxview){ //rewrite to a normal EnhancementRequest
+ return enhanceFromData(ci, null, false, null, false, null, false, null, headers);
+ } else { //enhance and build the AJAX response
+ enhance(ci);
+ ContentItemResource contentItemResource = new ContentItemResource(null, ci, uriInfo, "",
+ tcManager, serializer, servletContext);
+ contentItemResource.setRdfSerializationFormat(format);
+ Viewable ajaxView = new Viewable("/ajax/contentitem", contentItemResource);
+ ResponseBuilder rb = Response.ok(ajaxView);
+ rb.header(HttpHeaders.CONTENT_TYPE, TEXT_HTML + "; charset=UTF-8");
+ addCORSOrigin(servletContext, rb, headers);
+ return rb.build();
+ }
+ }
+
+ public boolean isEngineActive(String name) {
+ return engineManager.isEngine(name);
+ }
+
+ public String getServiceUrl() {
+ String uri = uriInfo.getAbsolutePath().toString();
+ return uri.charAt(uri.length()-1) == '/' ?
+ uri.substring(0, uri.length()-1) : uri;
+ }
+
+ /**
+ * Getter for the executionNodes
+ *
+ * @return
+ */
+ public Set<ExecutionNode> getExecutionNodes() {
+ if (_executionNodes == null) {
+ Graph ep;
+ try {
+ ep = chain.getExecutionPlan();
+ } catch (ChainException e) {
+ ep = null;
+ }
+ if (ep != null) {
+ _executionNodes = new LinkedHashSet<ExecutionNode>();
+ Set<NonLiteral> processed = new HashSet<NonLiteral>();
+ Set<NonLiteral> next;
+ do {
+ next = ExecutionPlanHelper.getExecutable(ep, processed);
+ for (NonLiteral node : next) {
+ _executionNodes.add(new ExecutionNode(ep, node));
+ }
+ processed.addAll(next);
+ } while (!next.isEmpty());
+ }
+ }
+ return _executionNodes;
+ }
+
+ public Set<ExecutionNode> getActiveNodes() {
+ if (_activeNodes == null) {
+ Set<ExecutionNode> ens = getExecutionNodes();
+ if (ens != null) {
+ _activeNodes = new LinkedHashSet<ExecutionNode>();
+ for (ExecutionNode en : ens) {
+ if (en.isEngineActive()) {
+ _activeNodes.add(en);
+ }
+ }
+ }
+ }
+ return _activeNodes;
+ }
+
+ public Chain getChain() {
+ return chain;
+ }
+
+ public boolean isChainAvailable() {
+ Set<ExecutionNode> nodes = getExecutionNodes();
+ if (nodes == null) {
+ return false;
+ }
+ for (ExecutionNode node : getExecutionNodes()) {
+ if (!node.isOptional() && !node.isEngineActive()) {
+ return false;
+ }
+ }
+ return true;
+ }
+ public class ExecutionNode {
+
+ private final NonLiteral node;
+ private final TripleCollection ep;
+ private final boolean optional;
+ private final String engineName;
+
+ public ExecutionNode(TripleCollection executionPlan, NonLiteral node) {
+ this.node = node;
+ this.ep = executionPlan;
+ this.optional = ExecutionPlanHelper.isOptional(ep, node);
+ this.engineName = ExecutionPlanHelper.getEngine(ep, node);
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public String getEngineName() {
+ return engineName;
+ }
+
+ public EnhancementEngine getEngine() {
+ return engineManager.getEngine(engineName);
+ }
+
+ public boolean isEngineActive() {
+ return engineManager.isEngine(engineName);
+ }
+
+ @Override
+ public int hashCode() {
+ return node.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ExecutionNode && ((ExecutionNode) o).node.equals(node);
+ }
+ }
+}
\ No newline at end of file