diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..05a73016427276249b29d6af6cad8646c6133420 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.settings +/.classpath diff --git a/.project b/.project new file mode 100644 index 0000000000000000000000000000000000000000..1d0ba7966df3f4531eb5665b12d3659fbe49dd51 --- /dev/null +++ b/.project @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>aggregator</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.wst.jsdt.core.javascriptValidator</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.wst.common.project.facet.core.builder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.wst.validation.validationbuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.m2e.core.maven2Builder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jem.workbench.JavaEMFNature</nature> + <nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.m2e.core.maven2Nature</nature> + <nature>org.eclipse.wst.common.project.facet.core.nature</nature> + <nature>org.eclipse.wst.jsdt.core.jsNature</nature> + </natures> +</projectDescription> diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..7cf4d6e24c77b04cf7b7d5c690ab055c5f60b75b --- /dev/null +++ b/pom.xml @@ -0,0 +1,285 @@ +<?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> + <groupId>info.textgrid.services</groupId> + <artifactId>aggregator</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>war</packaging> + <name>Simple CXF JAX-RS webapp service using spring configuration</name> + <description>Simple CXF JAX-RS webapp service using spring configuration</description> + <properties> + <jackson.version>1.9.7</jackson.version> + </properties> + + <developers> + <developer> + <id>vitt</id> + <name>Thorsten Vitt</name> + <email>thorsten.vitt@uni-wuerzburg.de</email> + <url>http://www.thorstenvitt.de/</url> + <organization>Universität Würzburg</organization> + <organizationUrl>http://www.germanistik.uni-wuerzburg.de/lehrstuehle/computerphilologie</organizationUrl> + <roles> + </roles> + </developer> + </developers> + + + <repositories> + <repository> + <id>bibforge.internal.http</id> + <name>Bibforge Managed Internal Repository</name> + <url>http://repository.bibforge.org/archiva/repository/internal</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>false</enabled> + </snapshots> + </repository> + <repository> + <id>bibforge.snapshots.http</id> + <name>Bibforge Managed Snapshot Repository</name> + <url>http://repository.bibforge.org/archiva/repository/snapshots</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </repository> + </repositories> + + + <dependencies> + <dependency> + <groupId>org.codehaus.jackson</groupId> + <artifactId>jackson-core-asl</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>org.codehaus.jackson</groupId> + <artifactId>jackson-mapper-asl</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>org.codehaus.jackson</groupId> + <artifactId>jackson-jaxrs</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.10</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>12.0</version> + </dependency> + <dependency> + <groupId>info.textgrid.middleware</groupId> + <artifactId>tgcrud-client</artifactId> + <version>2.3.4-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>info.textgrid.middleware</groupId> + <artifactId>tgsearch-client</artifactId> + <version>2.1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>info.textgrid.middleware</groupId> + <artifactId>confclient</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-frontend-jaxrs</artifactId> + <version>2.5.2</version> + </dependency> + </dependencies> + + + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>tomcat-maven-plugin</artifactId> + <version>1.1</version> + <executions> + <execution> + <id>default-cli</id> + <goals> + <goal>run</goal> + </goals> + <configuration> + <port>13000</port> + <path>/aggregator</path> + <useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.5</source> + <target>1.5</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-eclipse-plugin</artifactId> + <configuration> + <projectNameTemplate>[artifactId]-[version]</projectNameTemplate> + <wtpmanifest>true</wtpmanifest> + <wtpapplicationxml>true</wtpapplicationxml> + <wtpversion>2.0</wtpversion> + </configuration> + </plugin> + <!--This plugin's configuration is used to store Eclipse m2e settings + only. It has no influence on the Maven build itself. --> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + org.codehaus.mojo + </groupId> + <artifactId> + build-helper-maven-plugin + </artifactId> + <versionRange> + [1.5,) + </versionRange> + <goals> + <goal> + reserve-network-port + </goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore></ignore> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.3.1</version> + <configuration> + <source>1.6</source> + <target>1.6</target> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>1.5</version> + <executions> + <execution> + <id>reserve-network-port</id> + <goals> + <goal>reserve-network-port</goal> + </goals> + <phase>process-test-resources</phase> + <configuration> + <portNames> + <portName>test.server.port</portName> + </portNames> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>tomcat-maven-plugin</artifactId> + <executions> + <execution> + <id>start-tomcat</id> + <goals> + <goal>run-war</goal> + </goals> + <phase>pre-integration-test</phase> + <configuration> + <port>${test.server.port}</port> + <path>/aggregator</path> + <fork>true</fork> + <useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader> + </configuration> + </execution> + <execution> + <id>stop-tomcat</id> + <goals> + <goal>shutdown</goal> + </goals> + <phase>post-integration-test</phase> + <configuration> + <path>/aggregator</path> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + <version>2.8.1</version> + <executions> + <execution> + <id>integration-test</id> + <goals> + <goal>integration-test</goal> + </goals> + <configuration> + <systemPropertyVariables> + <service.url>http://localhost:${test.server.port}/aggregator</service.url> + </systemPropertyVariables> + </configuration> + </execution> + <execution> + <id>verify</id> + <goals> + <goal>verify</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>wue</id> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>tomcat-maven-plugin</artifactId> + <configuration> + <url>http://wrzh075.rzhousing.uni-wuerzburg.de:8180/manager</url> + <server>wrzh075</server> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <organization> + <name>TextGrid</name> + <url>http://www.textgrid.de/</url> + </organization> +</project> diff --git a/src/main/java/info/textgrid/services/aggregator/HelloWorld.java b/src/main/java/info/textgrid/services/aggregator/HelloWorld.java new file mode 100644 index 0000000000000000000000000000000000000000..392fc78298972fb9d644c30256bf6de5d28e2cc6 --- /dev/null +++ b/src/main/java/info/textgrid/services/aggregator/HelloWorld.java @@ -0,0 +1,29 @@ +package info.textgrid.services.aggregator; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Response; + +@Path("/hello") +public class HelloWorld { + + @GET + @Path("/echo/{input}") + @Produces("text/plain") + public String ping(@PathParam("input") String input) { + return input; + } + + @POST + @Produces("application/json") + @Consumes("application/json") + @Path("/jsonBean") + public Response modifyJson(JsonBean input) { + input.setVal2(input.getVal1()); + return Response.ok().entity(input).build(); + } +} + diff --git a/src/main/java/info/textgrid/services/aggregator/JsonBean.java b/src/main/java/info/textgrid/services/aggregator/JsonBean.java new file mode 100644 index 0000000000000000000000000000000000000000..72342b9be84ca8460f00992a0628effc6517b889 --- /dev/null +++ b/src/main/java/info/textgrid/services/aggregator/JsonBean.java @@ -0,0 +1,24 @@ +package info.textgrid.services.aggregator; + + +public class JsonBean { + private String val1; + private String val2; + + public String getVal1() { + return val1; + } + + public void setVal1(String val1) { + this.val1 = val1; + } + + public String getVal2() { + return val2; + } + + public void setVal2(String val2) { + this.val2 = val2; + } + +} diff --git a/src/main/java/info/textgrid/services/aggregator/TEICorpus.java b/src/main/java/info/textgrid/services/aggregator/TEICorpus.java new file mode 100644 index 0000000000000000000000000000000000000000..0786051cb0207a5cfcf8ce23b8298c25a0cf4eea --- /dev/null +++ b/src/main/java/info/textgrid/services/aggregator/TEICorpus.java @@ -0,0 +1,166 @@ +package info.textgrid.services.aggregator; + +import info.textgrid.middleware.confclient.ConfservClient; +import info.textgrid.middleware.confclient.ConfservClientConstants; +import info.textgrid.middleware.tgsearch.client.SearchClient; +import info.textgrid.namespaces.metadata.core._2010.MetadataContainerType; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.AuthFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.IoFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.MetadataParseFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.ObjectNotFoundFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.TGCrudService; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.TGCrudService_Service; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriBuilder; +import javax.xml.stream.XMLStreamException; +import javax.xml.ws.BindingProvider; +import javax.xml.ws.soap.MTOMFeature; + +import org.codehaus.jettison.json.JSONException; + +/* + * Generating a TEIcorpus document from a (single) aggregation + * + * Header: + * + * Header could be generated in the same way as in the metadata editor, i.e. + * paste together the ancestor metadata records (including matching work + * records) and then run an XSLT stylesheet. + * + * Children's Header: + * + * For descendant aggregations, we still need a header. Either we consider this + * redundant, or we preserve the metadata records we copied together for the + * respective ancestor levels, append the current level & run the + * transformation. + * + * Body: + * + * Naïve approach: Load and recursively process the aggregations, check each + * one's content type Probably better: SearchClient has a listAggregation() + * method that is also used by the navigator and that returns metadata records. + * We could use this, so it's only one large request per aggregation. Should be + * reasonably fast. + */ +@Path("/teicorpus") +public class TEICorpus { + + private static final String CONF_ENDPOINT = "https://www.textgridlab.org/1.0/confserv"; + private ConfservClient confservClient; + private SearchClient searchClient; + private HashMap<String, String> config; + private TGCrudService crud; + final Logger logger = Logger.getLogger("info.textgrid.services.aggregator"); + + @GET + @Path(value="/{aggregation}") + @Produces("text/xml") + public Response get(@PathParam("aggregation") final URI uri, @QueryParam("attach") @DefaultValue("true") final boolean attach) throws URISyntaxException { + logger.fine("TEIcorpus called for root aggregation: " + uri); + final TGCrudService crud = getCRUDService(); + logger.finest("Yo, clients are there."); + try { + final MetadataContainerType rootAggregationMetadata = crud.readMetadata(null, null, uri.toString()); + logger.finer("CRUD request for root aggregation successful"); + final TEICorpusSerializer serializer = new TEICorpusSerializer(rootAggregationMetadata.getObject(), this); + final String format = rootAggregationMetadata.getObject().getGeneric().getProvided().getFormat(); + if (!format.contains("aggregation")) { + logger.log(Level.SEVERE, "The requested object {0} is a {1}, not an aggregation, and thus cannot be aggregated.", new Object[] {uri,format}); + throw new WebApplicationException(javax.ws.rs.core.Response + .status(Status.BAD_REQUEST) + .entity(MessageFormat.format( + "The requested object {0} is a {1}, not an aggregation, and thus cannot be aggregated.", uri, + format)).build()); + } + ResponseBuilder builder = Response.ok(serializer, "text/xml"); + if (attach) { + String fileName = rootAggregationMetadata.getObject().getGeneric().getProvided().getTitle().get(0) + ".xml"; + String asciiFileName = fileName.replaceAll("[^A-Za-z0-9.-]", "_"); + String extendedNameSpec = "UTF-8''" + UriBuilder.fromPath(fileName).build().toASCIIString(); + builder.header("Content-Disposition", "attachment; filename*=" + extendedNameSpec + " "); + builder.header("Content-Disposition", " filename=\"" + asciiFileName + "\" " ); + } + return builder.build(); + } catch (final ObjectNotFoundFault e) { + logger.log(Level.SEVERE, "CRUD: (Root aggregation) not found", e); + throw new WebApplicationException(e, javax.ws.rs.core.Response.status(Status.NOT_FOUND).entity(e.toString()).build()); + } catch (final MetadataParseFault e) { + logger.log(Level.SEVERE, "CRUD: Could not parse metadata", e); + throw new WebApplicationException(e); + } catch (final IoFault e) { + logger.log(Level.SEVERE, "CRUD: IO Fault", e); + throw new WebApplicationException(e); + } catch (final AuthFault e) { + logger.log(Level.SEVERE, "CRUD: (Root aggregation) AuthFault", e); + throw new WebApplicationException(e, Status.FORBIDDEN); + } + + // final Response response = search.listAggregation(uri.toString()); + // for (final ResultType result : response.getResult()) { + // final String contentType = + // result.getObject().getGeneric().getProvided().getFormat(); + // if (contentType.contains("aggregation")) { + // // TODO deal with aggregation + // } else if ("text/xml".equals(contentType)) { + // // TODO deal with + // } + // } + // return null; + } + + TGCrudService getCRUDService() { + if (crud == null) { + final URL wsdl = TGCrudService_Service.class.getResource("/wsdl/TGCrudService.wsdl"); + final TGCrudService_Service tgCrudService_Service = new TGCrudService_Service(wsdl); + final TGCrudService _crud = tgCrudService_Service.getTGCrudPort(new MTOMFeature()); + final BindingProvider bp = (BindingProvider) _crud; + final Map<String, Object> requestContext = bp.getRequestContext(); + requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, getConfValue(ConfservClientConstants.TG_CRUD)); + crud = _crud; + } + return crud; + } + + SearchClient getSearchClient() { + if (searchClient == null) + searchClient = new SearchClient(getConfValue(ConfservClientConstants.TG_SEARCH_PUBLIC)); + return searchClient; + } + + String getConfValue(final String key) throws WebApplicationException { + if (config == null) { + try { + confservClient = new ConfservClient(CONF_ENDPOINT); + config = confservClient.getAll(); + } catch (final IOException e) { + throw new WebApplicationException(e); + } catch (final JSONException e) { + throw new WebApplicationException(e); + } catch (final XMLStreamException e) { + throw new WebApplicationException(e); + } + } + return config.get(key); + } + +} diff --git a/src/main/java/info/textgrid/services/aggregator/TEICorpusSerializer.java b/src/main/java/info/textgrid/services/aggregator/TEICorpusSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..74bb8a44bbf7a9762efea28fae6efb0f24719bbb --- /dev/null +++ b/src/main/java/info/textgrid/services/aggregator/TEICorpusSerializer.java @@ -0,0 +1,204 @@ +package info.textgrid.services.aggregator; + +import info.textgrid.namespaces.metadata.agent._2010.AgentType; +import info.textgrid.namespaces.metadata.core._2010.MetadataContainerType; +import info.textgrid.namespaces.metadata.core._2010.ObjectType; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.AuthFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.IoFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.MetadataParseFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.ObjectNotFoundFault; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.ProtocolNotImplementedFault; +import info.textgrid.namespaces.middleware.tgsearch.ResultType; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.activation.DataHandler; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; +import javax.xml.stream.EventFilter; +import javax.xml.stream.XMLEventFactory; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; +import javax.xml.ws.Holder; + +import com.google.common.base.Joiner; +import com.google.common.collect.Sets; + +public class TEICorpusSerializer implements StreamingOutput { + + private static final String TEI_CORPUS = "teiCorpus"; + private static final String TEI_NS = "http://www.tei-c.org/ns/1.0"; + private final Logger logger = Logger.getLogger("info.textgrid.services.aggregator.teicorpus.serializer"); + + private XMLOutputFactory outputFactory; + private XMLEventFactory eventFactory; + private XMLEventWriter writer; + private final ObjectType rootObject; + private final TEICorpus teiCorpus; + private XMLInputFactory inputFactory; + private final Set<String> seen = Sets.newHashSet(); + private static final EventFilter NO_DOCUMENT_NODE = new EventFilter() { + + @Override + public boolean accept(final XMLEvent event) { + return !(event.isStartDocument() || event.isEndDocument()); + } + }; + + private static String toString(final ObjectType object) { + return Joiner.on(" / ").join(object.getGeneric().getProvided().getTitle()) + " (" + object.getGeneric().getGenerated().getTextgridUri().getValue() + ", " + + object.getGeneric().getProvided().getFormat() + ")"; + } + + public TEICorpusSerializer(final ObjectType object, final TEICorpus teiCorpus) { + this.rootObject = object; + this.teiCorpus = teiCorpus; + logger.info("Starting TEIcorpus serialization for " + toString(object)); + } + + @Override + public void write(final OutputStream output) throws IOException, WebApplicationException { + outputFactory = XMLOutputFactory.newInstance(); + eventFactory = XMLEventFactory.newInstance(); + outputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); + inputFactory = XMLInputFactory.newInstance(); + try { + writer = outputFactory.createXMLEventWriter(output); + writer.setDefaultNamespace(TEI_NS); + writer.add(eventFactory.createStartDocument()); + + write(output, rootObject); + + writer.close(); + } catch (final XMLStreamException e) { + logger.log(Level.SEVERE, "XML Error during serialization", e); + throw new WebApplicationException(e); + } + } + + private void write(final OutputStream output, final ObjectType object) throws XMLStreamException { + if (seen.contains(object.getGeneric().getGenerated().getTextgridUri().getValue())) + writeSkip(output, object); + else + seen.add(object.getGeneric().getGenerated().getTextgridUri().getValue()); + + + if (object.getGeneric().getProvided().getFormat().contains("aggregation")) { + writeAggregation(output, object); + } else if (object.getGeneric().getProvided().getFormat().equals("text/xml")) { + writeXML(output, object); + } else + writeSkip(output, object); + } + + private void writeAggregation(final OutputStream output, final ObjectType object) throws XMLStreamException { + logger.fine("[Processing aggregation " + toString(object)); + final info.textgrid.namespaces.middleware.tgsearch.Response response = teiCorpus.getSearchClient().listAggregation( + object.getGeneric().getGenerated().getTextgridUri().getValue()); + final List<ResultType> result = response.getResult(); + + writer.add(eventFactory.createStartElement("", TEI_NS, TEI_CORPUS)); + writer.add(eventFactory.createAttribute("corresp", object.getGeneric().getGenerated().getTextgridUri().getValue())); + + writeHeader(object); + + for (final ResultType resultType : result) { + final ObjectType aggregate = resultType.getObject(); + if (aggregate != null) + write(output, aggregate); + } + + writer.add(eventFactory.createEndElement("", TEI_NS, TEI_CORPUS)); + logger.fine("Finished aggregation " + toString(object) + "]"); + } + + private void writeHeader(ObjectType object) throws XMLStreamException { + writer.add(eventFactory.createStartElement("", TEI_NS, "teiHeader")); + writer.add(eventFactory.createComment("This is a (temporary) header generated from aggregation metadata.")); + writer.add(eventFactory.createStartElement("", TEI_NS, "fileDesc")); + writer.add(eventFactory.createStartElement("", TEI_NS, "titleStmt")); + for (final String title : object.getGeneric().getProvided().getTitle()) { + writeSimpleTEIElement("title", title); + } + if (object.getEdition() != null) { + for (AgentType agent : object.getEdition().getAgent()) { + writer.add(eventFactory.createStartElement("", TEI_NS, "respStmt")); + writeSimpleTEIElement("resp", agent.getRole().toString()); + writer.add(eventFactory.createStartElement("", TEI_NS, "author")); + if (agent.getId() != null) + writer.add(eventFactory.createAttribute("key", agent.getId())); + writer.add(eventFactory.createCharacters(agent.getValue())); + writer.add(eventFactory.createEndElement("", TEI_NS, "author")); + writer.add(eventFactory.createEndElement("", TEI_NS, "respStmt")); + } + } + writer.add(eventFactory.createEndElement("", TEI_NS, "titleStmt")); + writer.add(eventFactory.createStartElement("", TEI_NS, "publicationStmt")); + writer.add(eventFactory.createStartElement("", TEI_NS, "idno")); + writer.add(eventFactory.createAttribute("type", "textgrid")); + writer.add(eventFactory.createCharacters(object.getGeneric().getGenerated().getTextgridUri().getValue())); + writer.add(eventFactory.createEndElement("", TEI_NS, "idno")); + writer.add(eventFactory.createEndElement("", TEI_NS, "publicationStmt")); + writer.add(eventFactory.createEndElement("", TEI_NS, "fileDesc")); + writer.add(eventFactory.createEndElement("", TEI_NS, "teiHeader")); + } + + private void writeSimpleTEIElement(String element, final String content) throws XMLStreamException { + writer.add(eventFactory.createStartElement("", TEI_NS, element)); + writer.add(eventFactory.createCharacters(content)); + writer.add(eventFactory.createEndElement("", TEI_NS, element)); + } + + private void writeXML(final OutputStream output, final ObjectType object) { + logger.fine("Processing XML " + toString(object)); + final Holder<MetadataContainerType> mdHolder = new Holder<MetadataContainerType>(); + final Holder<DataHandler> dhHolder = new Holder<DataHandler>(); + try { + teiCorpus.getCRUDService().read("", "", object.getGeneric().getGenerated().getTextgridUri().getValue(), mdHolder, + dhHolder); + final XMLEventReader reader = inputFactory.createFilteredReader( + inputFactory.createXMLEventReader(dhHolder.value.getInputStream()), + NO_DOCUMENT_NODE); + + // skip to document node: + while (reader.hasNext() && !reader.peek().isStartElement()) + reader.next(); + + writer.add(reader); + } catch (final ObjectNotFoundFault e) { + throw new WebApplicationException(e, Response.Status.NOT_FOUND); + } catch (final MetadataParseFault e) { + throw new WebApplicationException(e); + } catch (final IoFault e) { + throw new WebApplicationException(e); + } catch (final ProtocolNotImplementedFault e) { + throw new WebApplicationException(e); + } catch (final AuthFault e) { + throw new WebApplicationException(e, Response.Status.FORBIDDEN); + } catch (final XMLStreamException e) { + throw new WebApplicationException(e); + } catch (final IOException e) { + throw new WebApplicationException(e, Response.Status.SERVICE_UNAVAILABLE); + } + } + + /** + * Writes a comment for objects that are skipped. + */ + private void writeSkip(final OutputStream output, final ObjectType object) throws XMLStreamException { + logger.fine("Skipped " + toString(object)); + writer.add(eventFactory.createComment(MessageFormat.format("Skipped {0}.", toString(object)))); + } + +} diff --git a/src/main/webapp/WEB-INF/beans.xml b/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000000000000000000000000000000000000..5499619b6f729d70ddf7fb04bfe0111bc509f665 --- /dev/null +++ b/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:jaxrs="http://cxf.apache.org/jaxrs" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation=" +http://www.springframework.org/schema/beans +http://www.springframework.org/schema/beans/spring-beans-3.0.xsd +http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd +http://cxf.apache.org/jaxrs +http://cxf.apache.org/schemas/jaxrs.xsd"> + + <import resource="classpath:META-INF/cxf/cxf.xml" /> + <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> + <context:property-placeholder/> + <context:annotation-config/> + <bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer"/> + <bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer"/> + + + <jaxrs:server id="services" address="/"> + <jaxrs:serviceBeans> + <!-- bean class="info.textgrid.services.aggregator.HelloWorld" /--> + <bean class="info.textgrid.services.aggregator.TEICorpus" scope="singleton" /> + </jaxrs:serviceBeans> + <jaxrs:providers> + <!-- bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/--> + <bean class="org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper"/> + </jaxrs:providers> + + </jaxrs:server> + +</beans> diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000000000000000000000000000000000..b6dd67f341e6e18b5e5ce5cfa951364d10e046dd --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<web-app + xmlns="http://java.sun.com/xml/ns/j2ee" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" + version="2.4"> + <display-name>TextGrid Aggregator Service</display-name> + <description>A service that extracts TextGrid data by aggregations, + recursively</description> + + <context-param> + <param-name>contextConfigLocation</param-name> + <param-value>WEB-INF/beans.xml</param-value> + </context-param> + + <listener> + <listener-class> + org.springframework.web.context.ContextLoaderListener + </listener-class> + </listener> + + <servlet> + <servlet-name>CXFServlet</servlet-name> + <servlet-class> + org.apache.cxf.transport.servlet.CXFServlet + </servlet-class> + <load-on-startup>1</load-on-startup> + </servlet> + + <servlet-mapping> + <servlet-name>CXFServlet</servlet-name> + <url-pattern>/*</url-pattern> + </servlet-mapping> +</web-app> diff --git a/src/test/java/info/textgrid/services/aggregator/HelloWorldIT.java b/src/test/java/info/textgrid/services/aggregator/HelloWorldIT.java new file mode 100644 index 0000000000000000000000000000000000000000..40ada9ede2dfdae8a7ff95f07bff4af07dffff3b --- /dev/null +++ b/src/test/java/info/textgrid/services/aggregator/HelloWorldIT.java @@ -0,0 +1,49 @@ +package info.textgrid.services.aggregator; + +import static org.junit.Assert.assertEquals; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.core.Response; + +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.jaxrs.client.WebClient; +import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.map.MappingJsonFactory; + +public class HelloWorldIT { + private static String endpointUrl; + + // @BeforeClass + public static void beforeClass() { + endpointUrl = System.getProperty("service.url"); + } + + // @Test + public void testPing() throws Exception { + final WebClient client = WebClient.create(endpointUrl + "/hello/echo/SierraTangoNevada"); + final Response r = client.accept("text/plain").get(); + assertEquals(Response.Status.OK.getStatusCode(), r.getStatus()); + final String value = IOUtils.toString((InputStream)r.getEntity()); + assertEquals("SierraTangoNevada", value); + } + + // @Test + public void testJsonRoundtrip() throws Exception { + final List<Object> providers = new ArrayList<Object>(); + providers.add(new org.codehaus.jackson.jaxrs.JacksonJsonProvider()); + final JsonBean inputBean = new JsonBean(); + inputBean.setVal1("Maple"); + final WebClient client = WebClient.create(endpointUrl + "/hello/jsonBean", providers); + final Response r = client.accept("application/json") + .type("application/json") + .post(inputBean); + assertEquals(Response.Status.OK.getStatusCode(), r.getStatus()); + final MappingJsonFactory factory = new MappingJsonFactory(); + final JsonParser parser = factory.createJsonParser((InputStream)r.getEntity()); + final JsonBean output = parser.readValueAs(JsonBean.class); + assertEquals("Maple", output.getVal2()); + } +} diff --git a/src/test/java/info/textgrid/services/aggregator/TEICorpusTest.java b/src/test/java/info/textgrid/services/aggregator/TEICorpusTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f635f062e89fb22b5a92ea901e5a74e561a0d60f --- /dev/null +++ b/src/test/java/info/textgrid/services/aggregator/TEICorpusTest.java @@ -0,0 +1,41 @@ +package info.textgrid.services.aggregator; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import info.textgrid.middleware.confclient.ConfservClientConstants; +import info.textgrid.middleware.tgsearch.client.SearchClient; +import info.textgrid.namespaces.middleware.tgcrud.services.tgcrudservice.TGCrudService; + +import org.junit.Test; + +public class TEICorpusTest { + + private TEICorpus teicorpus; + + TEICorpus getTeiCorpus() { + if (teicorpus == null) + teicorpus = new TEICorpus(); + return teicorpus; + } + + @Test + public void testGetConfValue() { + final String confValue = getTeiCorpus().getConfValue(ConfservClientConstants.TG_SEARCH_PUBLIC); + assertNotNull("Configuration value is null.", confValue); + assertEquals("https://textgridlab.org/1.0/tgsearch-public", confValue); + } + + @Test + public void testGetCRUDService() { + final TGCrudService crudService = getTeiCorpus().getCRUDService(); + assertNotNull("TG-crud service is null!?", crudService); + } + + @Test + public void testGetSearchClient() { + final SearchClient searchClient = getTeiCorpus().getSearchClient(); + assertNotNull("TG-search service is null!?", searchClient); + } + + +}