/**
* This file is part of d:swarm graph extension.
*
* d:swarm graph extension is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* d:swarm graph extension is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with d:swarm graph extension. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dswarm.graph.resources;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Optional;
import org.neo4j.graphdb.GraphDatabaseService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.dswarm.common.DMPStatics;
import org.dswarm.common.model.AttributePath;
import org.dswarm.common.model.util.AttributePathUtil;
import org.dswarm.graph.DMPGraphException;
import org.dswarm.graph.index.NamespaceIndex;
import org.dswarm.graph.tx.Neo4jTransactionHandler;
import org.dswarm.graph.tx.TransactionHandler;
import org.dswarm.graph.xml.read.PropertyGraphXMLReader;
import org.dswarm.graph.xml.read.XMLReader;
/**
* TODO: refactor the design of the resources. this is not RESTy atm, i.e., there should be one pattern how the receive a certain
* data model from this unmanaged extension and depending on the Accept-Type of the request the result will be in this format
* (media type), i.e., "gdm", various RDF serialisation, XML, or whatever ... => i.e., we don't need separate resources
* (endpoints) for certain descriptions (abstract formats) or representations (concrete formats, mediatypes, serialisations)
*
* @author tgaengler
*/
@Path("/xml")
public class XMLResource {
private static final Logger LOG = LoggerFactory.getLogger(XMLResource.class);
/**
* The object mapper that can be utilised to de-/serialise JSON nodes.
*/
private final ObjectMapper objectMapper;
public XMLResource() {
objectMapper = new ObjectMapper();
}
@GET
@Path("/ping")
public String ping() {
XMLResource.LOG.debug("ping was called");
return "pong";
}
@POST
@Path("/get")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_XML)
public Response readXML(final String jsonObjectString, @Context final GraphDatabaseService database) throws DMPGraphException {
XMLResource.LOG.debug("try to read XML records from graph db");
final ObjectNode json;
try {
json = objectMapper.readValue(jsonObjectString, ObjectNode.class);
} catch (final IOException e) {
final String message = "could not deserialise request JSON for read from graph DB request";
XMLResource.LOG.debug(message);
throw new DMPGraphException(message, e);
}
final String recordClassUri = json.get(DMPStatics.RECORD_CLASS_URI_IDENTIFIER).asText();
final String dataModelUri = json.get(DMPStatics.DATA_MODEL_URI_IDENTIFIER).asText();
final JsonNode rootAttributePathNode = json.get(DMPStatics.ROOT_ATTRIBUTE_PATH_IDENTIFIER);
final Optional<AttributePath> optionalRootAttributePath = Optional.fromNullable(AttributePathUtil
.parseAttributePathNode(rootAttributePathNode));
final Optional<String> optionalRecordTag;
final JsonNode recordTagNode = json.get(DMPStatics.RECORD_TAG_IDENTIFIER);
if (recordTagNode != null) {
optionalRecordTag = Optional.fromNullable(recordTagNode.asText());
} else {
optionalRecordTag = Optional.absent();
}
final JsonNode versionNode = json.get(DMPStatics.VERSION_IDENTIFIER);
final Optional<Integer> optionalVersion;
if (versionNode != null) {
optionalVersion = Optional.fromNullable(versionNode.asInt());
} else {
optionalVersion = Optional.absent();
}
final JsonNode allVersionsNode = json.get(DMPStatics.ALL_VERSIONS_IDENTIFIER);
final Optional<Boolean> optionalAllversion;
if (allVersionsNode != null) {
optionalAllversion = Optional.fromNullable(allVersionsNode.asBoolean());
} else {
optionalAllversion = Optional.absent();
}
final Optional<JsonNode> optionalOriginalDataTypeNode = Optional.fromNullable(json.get(DMPStatics.ORIGINAL_DATA_TYPE_IDENTIFIER));
final Optional<String> optionalOriginalDataType;
if (optionalOriginalDataTypeNode.isPresent()) {
final Optional<String> optionalOriginalDataTypeFromJSON = Optional.fromNullable(optionalOriginalDataTypeNode.get().asText());
if (optionalOriginalDataTypeFromJSON.isPresent()) {
optionalOriginalDataType = optionalOriginalDataTypeFromJSON;
} else {
optionalOriginalDataType = Optional.absent();
}
} else {
optionalOriginalDataType = Optional.absent();
}
LOG.debug("try to read XML records for data model uri = '{}' and record class uri = '{}' from graph db", dataModelUri, recordClassUri);
final TransactionHandler tx = new Neo4jTransactionHandler(database);
final NamespaceIndex namespaceIndex = new NamespaceIndex(database, tx);
final XMLReader xmlReader = new PropertyGraphXMLReader(optionalRootAttributePath, optionalRecordTag, recordClassUri, dataModelUri, optionalVersion, optionalAllversion,
optionalOriginalDataType, database, tx, namespaceIndex);
final StreamingOutput stream = new StreamingOutput() {
@Override
public void write(final OutputStream os) throws IOException, WebApplicationException {
try {
final BufferedOutputStream bos = new BufferedOutputStream(os, 1024);
final Optional<XMLStreamWriter> optionalWriter = xmlReader.read(bos);
if (optionalWriter.isPresent()) {
optionalWriter.get().flush();
optionalWriter.get().close();
LOG.debug("finished reading '{}' XML records for data model uri = '{}' and record class uri = '{}' from graph db",
xmlReader.recordCount(), dataModelUri, recordClassUri);
} else {
bos.close();
os.close();
LOG.debug("couldn't find any XML records for data model uri = '{}' and record class uri = '{}' from graph db", dataModelUri,
recordClassUri);
}
} catch (final DMPGraphException | XMLStreamException e) {
throw new WebApplicationException(e);
}
}
};
return Response.ok(stream, MediaType.APPLICATION_XML_TYPE).build();
}
}