1.x branch (first created for gCube 2.12)
git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/branches/information-system/discovery-client/1.0@67186 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
commit
08dd818774
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>discovery-client</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
gCube System - License
|
||||
------------------------------------------------------------
|
||||
|
||||
The gCube/gCore software is licensed as Free Open Source software conveying to the EUPL (http://ec.europa.eu/idabc/eupl).
|
||||
The software and documentation is provided by its authors/distributors "as is" and no expressed or
|
||||
implied warranty is given for its use, quality or fitness for a particular case.
|
|
@ -0,0 +1,2 @@
|
|||
* Fabio Simeoni (fabio.simeoni@fao.org), FAO of the UN, Italy
|
||||
* Lucio Lelii (lucio.lelii@isti.cnr.it), CNR, Italy
|
|
@ -0,0 +1,39 @@
|
|||
The gCube System - ${name}
|
||||
----------------------
|
||||
|
||||
This work has been partially supported by the following European projects: DILIGENT (FP6-2003-IST-2), D4Science (FP7-INFRA-2007-1.2.2),
|
||||
D4Science-II (FP7-INFRA-2008-1.2.2), iMarine (FP7-INFRASTRUCTURES-2011-2), and EUBrazilOpenBio (FP7-ICT-2011-EU-Brazil).
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
* Fabio Simeoni (fabio.simeoni@fao.org), FAO of the UN, Italy
|
||||
* Lucio Lelii (lucio.lelii@isti.cnr.it), CNR, Italy
|
||||
|
||||
Version and Release Date
|
||||
------------------------
|
||||
${version}
|
||||
|
||||
Description
|
||||
-----------
|
||||
${description}
|
||||
|
||||
Download information
|
||||
--------------------
|
||||
|
||||
Source code is available from SVN:
|
||||
${scm.url}
|
||||
|
||||
Binaries can be downloaded from:
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Documentation is available on-line from the Projects Documentation Wiki:
|
||||
https://gcube.wiki.gcube-system.org/gcube/index.php
|
||||
|
||||
|
||||
Licensing
|
||||
---------
|
||||
|
||||
This software is licensed under the terms you may find in the file named "LICENSE" in this directory.
|
|
@ -0,0 +1,5 @@
|
|||
<ReleaseNotes>
|
||||
<Changeset component="${build.finalName}" date="11/01/2013">
|
||||
<Change>First Release</Change>
|
||||
</Changeset>
|
||||
</ReleaseNotes>
|
|
@ -0,0 +1,38 @@
|
|||
<assembly
|
||||
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
<id>servicearchive</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<baseDirectory>/</baseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${distroDirectory}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<includes>
|
||||
<include>README</include>
|
||||
<include>LICENSE</include>
|
||||
<include>INSTALL</include>
|
||||
<include>MAINTAINERS</include>
|
||||
<include>changelog.xml</include>
|
||||
<include>profile.xml</include>
|
||||
</includes>
|
||||
<fileMode>755</fileMode>
|
||||
<filtered>true</filtered>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<files>
|
||||
<file>
|
||||
<source>target/${build.finalName}.jar</source>
|
||||
<outputDirectory>/${artifactId}</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${distroDirectory}/svnpath.txt</source>
|
||||
<outputDirectory>/${artifactId}</outputDirectory>
|
||||
<filtered>true</filtered>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<ID />
|
||||
<Type>Service</Type>
|
||||
<Profile>
|
||||
<Description>${description}</Description>
|
||||
<Class>InformationSystem</Class>
|
||||
<Name>${artifactId}</Name>
|
||||
<Version>1.0.0</Version>
|
||||
<Packages>
|
||||
<Software>
|
||||
<Name>${artifactId}</Name>
|
||||
<Version>${version}</Version>
|
||||
<MavenCoordinates>
|
||||
<groupId>${groupId}</groupId>
|
||||
<artifactId>${artifactId}</artifactId>
|
||||
<version>${version}</version>
|
||||
</MavenCoordinates>
|
||||
<Files>
|
||||
<File>${build.finalName}.jar</File>
|
||||
</Files>
|
||||
</Software>
|
||||
</Packages>
|
||||
</Profile>
|
||||
</Resource>
|
||||
|
|
@ -0,0 +1 @@
|
|||
${scm.url}
|
|
@ -0,0 +1,103 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.gcube.tools</groupId>
|
||||
<artifactId>maven-parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>org.gcube.resources.discovery</groupId>
|
||||
<artifactId>discovery-client</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<name>Discovery Client</name>
|
||||
<description>Base API for resource discovery clients</description>
|
||||
|
||||
<scm>
|
||||
<connection>scm:svn:http://svn.d4science.research-infrastructures.eu/gcube/trunk/information-system/${project.artifactId}</connection>
|
||||
<developerConnection>scm:svn:https://svn.d4science.research-infrastructures.eu/gcube/trunk/information-system/${project.artifactId}</developerConnection>
|
||||
<url>http://svn.d4science.research-infrastructures.eu/gcube/trunk/information-system/${project.artifactId}</url>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<distroDirectory>distro</distroDirectory>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.data.access</groupId>
|
||||
<artifactId>streams</artifactId>
|
||||
<version>[2.0.0-SNAPSHOT,3.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.6.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-profile</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${distroDirectory}</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>profile.xml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>${distroDirectory}/descriptor.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>servicearchive</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,41 @@
|
|||
package org.gcube.resources.discovery.client.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.resources.discovery.client.queries.api.Query;
|
||||
|
||||
/**
|
||||
* Local interface for resource discovery.
|
||||
* <p>
|
||||
* Submits {@link Query}s for remote execution and returns a list of typed results.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <R> the type of query results
|
||||
*
|
||||
*/
|
||||
public interface DiscoveryClient<R> {
|
||||
|
||||
/**
|
||||
* Submits a {@link Query} for remote execution and returns a list of typed results.
|
||||
*
|
||||
* @param query the query
|
||||
* @return the results
|
||||
* @throws DiscoveryException if the query cannot be submitted
|
||||
* @throws InvalidResultException if the results cannot be parsed. Implementations may adopt different degrees of
|
||||
* tolerance to parsing errors before raising this exception.
|
||||
*/
|
||||
List<R> submit(Query query) throws DiscoveryException, InvalidResultException;
|
||||
|
||||
/**
|
||||
* Submits a {@link Query} for remote execution and returns a {@link Stream} of typed results.
|
||||
* <p>
|
||||
* Parsing errors may and should be delivered as {@link InvalidResultException}s during stream iteration.
|
||||
*
|
||||
* @param query the query
|
||||
* @return the results
|
||||
* @throws DiscoveryException if the query cannot be submitted
|
||||
*/
|
||||
Stream<R> submitForStream(Query query) throws DiscoveryException;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package org.gcube.resources.discovery.client.api;
|
||||
|
||||
|
||||
/**
|
||||
* Raised when services endpoints cannot be discovered.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class DiscoveryException extends RuntimeException {
|
||||
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an instance with a message.
|
||||
* @param msg the message
|
||||
*/
|
||||
public DiscoveryException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance from a cause.
|
||||
* @param cause the cause
|
||||
*/
|
||||
public DiscoveryException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance from a message and a cause.
|
||||
* @param msg the message
|
||||
* @param cause the cause
|
||||
*/
|
||||
public DiscoveryException(String msg,Throwable cause) {
|
||||
super(msg,cause);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package org.gcube.resources.discovery.client.api;
|
||||
|
||||
/**
|
||||
* Raised by {@link DiscoveryClient}s for result parsing errors.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class InvalidResultException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an instance with a given message.
|
||||
* @param msg the message
|
||||
*/
|
||||
public InvalidResultException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with a given cause.
|
||||
* @param cause the cause
|
||||
*/
|
||||
public InvalidResultException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with a given message and a given cause.
|
||||
* @param msg the message
|
||||
* @param cause the cause
|
||||
*/
|
||||
public InvalidResultException(String msg, Throwable cause) {
|
||||
super(msg,cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package org.gcube.resources.discovery.client.api;
|
||||
|
||||
import org.gcube.resources.discovery.client.queries.api.Query;
|
||||
|
||||
/**
|
||||
* Transforms untyped results into typed results.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <R> the result type
|
||||
*
|
||||
* @see DiscoveryClient
|
||||
* @see Query
|
||||
*/
|
||||
public interface ResultParser<R> {
|
||||
|
||||
/**
|
||||
* Transforms an untyped result.
|
||||
*
|
||||
* @param result the untyped results
|
||||
* @return the typed result
|
||||
* @throws Exception if the result cannot be typed
|
||||
*/
|
||||
R parse(String result) throws Exception;
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package org.gcube.resources.discovery.client.impl;
|
||||
|
||||
import static org.gcube.data.streams.dsl.Streams.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
import org.gcube.data.streams.exceptions.StreamStopSignal;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
import org.gcube.resources.discovery.client.api.DiscoveryClient;
|
||||
import org.gcube.resources.discovery.client.api.DiscoveryException;
|
||||
import org.gcube.resources.discovery.client.api.InvalidResultException;
|
||||
import org.gcube.resources.discovery.client.api.ResultParser;
|
||||
import org.gcube.resources.discovery.client.queries.api.Query;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A {@link DiscoveryClient} that delegates the execution of queries to another {@link DiscoveryClient} that
|
||||
* does not perform result parsing and the parsing itself to a dedicated {@link ResultParser}.
|
||||
*
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <R> the type of query results
|
||||
*/
|
||||
public class DelegateClient<R> implements DiscoveryClient<R> {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(DelegateClient.class);
|
||||
|
||||
private final ResultParser<R> parser;
|
||||
private final DiscoveryClient<String> inner;
|
||||
|
||||
/**
|
||||
* Creates an instance with a given {@link ResultParser} and a {@link DiscoveryClient} that produces untyped results
|
||||
* @param parser the parser
|
||||
* @param inner the client
|
||||
*/
|
||||
public DelegateClient(ResultParser<R> parser,DiscoveryClient<String> inner) {
|
||||
this.parser=parser;
|
||||
this.inner=inner;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* Result parsing errors are only logged as long as some results are successfully parsed. Otherwise, the
|
||||
* client flags the parsing errors as likely due to the parser itself.
|
||||
*/
|
||||
public List<R> submit(Query query) throws DiscoveryException, InvalidResultException {
|
||||
|
||||
List<R> parsed = new ArrayList<R>();
|
||||
|
||||
List<String> unparsed = inner.submit(query);
|
||||
|
||||
int errors = 0;
|
||||
|
||||
for (String result : unparsed)
|
||||
try {
|
||||
parsed.add(parser.parse(result));
|
||||
}
|
||||
catch(Exception e) {
|
||||
log.warn("discarded invalid result "+result,e);
|
||||
errors++;
|
||||
}
|
||||
|
||||
if (errors>0 && parsed.size()==0)
|
||||
throw new InvalidResultException("no success but "+errors+" errors parsing results");
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* Result parsing errors are only logged as long as they do results are successfully parsed. Otherwise, the
|
||||
* client flags the parsing errors as likely due to the parser itself.
|
||||
*/
|
||||
public Stream<R> submitForStream(Query query) throws DiscoveryException {
|
||||
|
||||
Stream<String> unparsed = inner.submitForStream(query);
|
||||
|
||||
return pipe(unparsed).through(new ParsingGenerator());
|
||||
|
||||
}
|
||||
|
||||
//helper
|
||||
private class ParsingGenerator implements Generator<String,R> {
|
||||
|
||||
public R yield(String result) throws StreamSkipSignal, StreamStopSignal {
|
||||
try {
|
||||
return parser.parse(result);
|
||||
}
|
||||
catch(Exception e) {
|
||||
throw new InvalidResultException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package org.gcube.resources.discovery.client.impl;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
|
||||
import org.gcube.resources.discovery.client.api.ResultParser;
|
||||
|
||||
/**
|
||||
* A {@link ResultParser} that parses query results into JAXB annotated classes.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <R> the type of parsed results
|
||||
*/
|
||||
public class JAXBParser<R> implements ResultParser<R> {
|
||||
|
||||
private final Class<R> type;
|
||||
private final Unmarshaller um;
|
||||
|
||||
//caches contexts per type
|
||||
private static Map<Class<?>,JAXBContext> ctxts = new HashMap<Class<?>, JAXBContext>();
|
||||
|
||||
/**
|
||||
* Creates an instance with a JAXB-annotated class.
|
||||
* @param type the class
|
||||
*/
|
||||
public JAXBParser(Class<R> type) {
|
||||
|
||||
this.type=type;
|
||||
|
||||
//lazily create unmarshaller for this type
|
||||
try {
|
||||
JAXBContext ctx = ctxts.get(type);
|
||||
if (ctx==null) {
|
||||
ctx = JAXBContext.newInstance(type);
|
||||
ctxts.put(type,ctx);
|
||||
}
|
||||
this.um=ctx.createUnmarshaller();
|
||||
}
|
||||
catch(Exception e) {
|
||||
throw new RuntimeException("error with parser",e);
|
||||
}
|
||||
}
|
||||
|
||||
public R parse(String result) throws Exception {
|
||||
return type.cast(um.unmarshal(new StringReader(result)));
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.gcube.resources.discovery.client.queries.api;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A query for resources.
|
||||
* <p>
|
||||
* The interface is intended for clients that consume queries and are not otherwise concerned with their construction.
|
||||
*
|
||||
*/
|
||||
public interface Query {
|
||||
|
||||
/**
|
||||
* Returns the textual expression of the query.
|
||||
* @return the expression.
|
||||
*/
|
||||
public String expression();
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.gcube.resources.discovery.client.queries.api;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A {@link Query} that can be customised with namespace declarations, conditions on results, and result expressions.
|
||||
*
|
||||
*/
|
||||
public interface SimpleQuery extends Query {
|
||||
|
||||
/**
|
||||
* Adds a variable to the query.
|
||||
*
|
||||
* @param name the name of the variable
|
||||
* @param range the range of the variable
|
||||
* @return the query
|
||||
*/
|
||||
SimpleQuery addVariable(String name, String range);
|
||||
|
||||
/**
|
||||
* Adds a free-form condition on query results.
|
||||
*
|
||||
* @param condition the condition
|
||||
* @return the query
|
||||
*/
|
||||
SimpleQuery addCondition(String condition);
|
||||
|
||||
/**
|
||||
* Adds a namespace to the query.
|
||||
* @param prefix the namespace prefix
|
||||
* @param uri the namespace URI
|
||||
* @return the query
|
||||
*/
|
||||
SimpleQuery addNamespace(String prefix, URI uri);
|
||||
|
||||
/**
|
||||
* Adds a result expression to the query.
|
||||
* @param expression the result expression
|
||||
* @return the query
|
||||
*/
|
||||
SimpleQuery setResult(String expression);
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package org.gcube.resources.discovery.client.queries.impl;
|
||||
|
||||
import static org.gcube.resources.discovery.client.queries.impl.Utils.*;
|
||||
|
||||
import org.gcube.resources.discovery.client.queries.api.Query;
|
||||
|
||||
public class QueryBox implements Query {
|
||||
|
||||
private final String expression;
|
||||
|
||||
public QueryBox(String expression) {
|
||||
notNull("expression",expression);
|
||||
this.expression=expression;
|
||||
}
|
||||
|
||||
public String expression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString()+"="+expression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((expression == null) ? 0 : expression.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
QueryBox other = (QueryBox) obj;
|
||||
if (expression == null) {
|
||||
if (other.expression != null)
|
||||
return false;
|
||||
} else if (!expression.equals(other.expression))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
package org.gcube.resources.discovery.client.queries.impl;
|
||||
|
||||
import static javax.xml.stream.XMLStreamConstants.*;
|
||||
import static org.gcube.resources.discovery.client.queries.impl.Utils.*;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
|
||||
import org.gcube.resources.discovery.client.queries.api.Query;
|
||||
|
||||
/**
|
||||
* A {@link Query} that interpolates named parameters inside a template.
|
||||
* <p>
|
||||
* Templates are strings with empty XML elements, optionally with a {@link #DEFAULT} attribute, e.g.:
|
||||
* <p>
|
||||
* <code>all results that satisfy <cond1/> or <cond2 def='that'/> <extra/></code>
|
||||
* <p>
|
||||
* Whenever {@link #expression()} is invoked, the elements in the template are replaced according to the first rule that applies
|
||||
* among the following:
|
||||
*
|
||||
* <ul>
|
||||
* <li>by the value of an equally named parameter, if one exists
|
||||
* <li>by the value of the {@link #DEFAULT} attribute, if one exists
|
||||
* <li>by the empty string
|
||||
* </ul>
|
||||
*
|
||||
* For example, given the previous template and the single parameter <code>cond1="this"</code>, {@link #expression()} returns:
|
||||
* <p>
|
||||
* <code>all results that satisfy this or that</code>
|
||||
* <p>
|
||||
*
|
||||
*/
|
||||
public class QueryTemplate extends QueryBox implements Query {
|
||||
|
||||
public static final String DEFAULT = "def";
|
||||
|
||||
|
||||
private static final XMLInputFactory xmlif = XMLInputFactory.newInstance();
|
||||
|
||||
private static final String wrapper = "_template_";
|
||||
|
||||
|
||||
private final Map<String, String> parameters;
|
||||
|
||||
/**
|
||||
* Creates an instance with a template.
|
||||
*
|
||||
* @param template the template
|
||||
*/
|
||||
public QueryTemplate(String template) {
|
||||
super(template);
|
||||
this.parameters = new HashMap<String, String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with a template and an initial set of parameters.
|
||||
*
|
||||
* @param template the template
|
||||
*/
|
||||
public QueryTemplate(String template, Map<String, String> parameters) {
|
||||
super(template);
|
||||
notNull("parameters", parameters);
|
||||
this.parameters = new HashMap<String, String>(parameters);
|
||||
}
|
||||
|
||||
public String expression() {
|
||||
return interpolate(super.expression(), parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parameter to the query, overwriting any value that it may already have.
|
||||
*
|
||||
* @param name the parameter name
|
||||
* @param value the parameter value
|
||||
* @throws IllegalStateException if the parameter name or value are <code>null</code>
|
||||
*/
|
||||
public void addParameter(String name, String value) {
|
||||
|
||||
notNull("name",name);
|
||||
notNull("value",value);
|
||||
|
||||
this.parameters.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parameter to the query, extending any value that it may already have.
|
||||
*
|
||||
* @param name the parameter name
|
||||
* @param value the value
|
||||
* @throws IllegalStateException if the parameter name or value are <code>null</code>
|
||||
*/
|
||||
public void appendParameter(String name, String value) {
|
||||
|
||||
notNull("name",name);
|
||||
notNull("value",value);
|
||||
|
||||
|
||||
if (parameters.containsKey(name))
|
||||
value=parameters.get(name)+value;
|
||||
|
||||
parameters.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of a parameter.
|
||||
* @param name the parameter name
|
||||
* @return the value
|
||||
* @throws IllegalStateException if the parameter does not exist
|
||||
* @throws IllegalStateException if the parameter name is <code>null</code>
|
||||
*/
|
||||
public String parameter(String name) throws IllegalStateException {
|
||||
|
||||
notNull("name",name);
|
||||
|
||||
if (hasParameter(name))
|
||||
return parameters.get(name);
|
||||
|
||||
throw new IllegalStateException("unknown parameter "+name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the query has a given parameter.
|
||||
* @param name the parameter name
|
||||
* @return <code>true</code> if the query has a given parameter, <code>false</code> otherwise
|
||||
* @throws IllegalStateException if the parameter name is <code>null</code>
|
||||
*/
|
||||
public boolean hasParameter(String name) {
|
||||
|
||||
notNull("name",name);
|
||||
|
||||
return parameters.containsKey(name);
|
||||
}
|
||||
|
||||
// helper
|
||||
private String interpolate(String expression, Map<String, String> parameters) {
|
||||
// replace query parameters with their values.
|
||||
try {
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
XMLStreamReader xmlr = xmlif.createXMLStreamReader(new StringReader("<"+wrapper+">"+expression+"</"+wrapper+">"));
|
||||
|
||||
loop: while (true) {
|
||||
|
||||
int tokenType = xmlr.next();
|
||||
|
||||
switch (tokenType) {
|
||||
|
||||
case START_ELEMENT: // replace parameters with values (provided or default)
|
||||
|
||||
String name = xmlr.getLocalName();
|
||||
|
||||
if (name.equals(wrapper))
|
||||
break;
|
||||
|
||||
if (parameters.containsKey(name))
|
||||
builder.append(parameters.get(name));
|
||||
else {
|
||||
// is there a default value?
|
||||
String def = xmlr.getAttributeValue(null,DEFAULT);
|
||||
if (def != null)
|
||||
// add default as a parameter
|
||||
builder.append(def);
|
||||
}
|
||||
break;
|
||||
|
||||
case CHARACTERS: // copy text in output
|
||||
builder.append(xmlr.getText());
|
||||
break;
|
||||
|
||||
case END_DOCUMENT:
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("cannot replace parameters " + parameters + " in query " + expression,e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.gcube.resources.discovery.client.queries.impl;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static void notNull(String name, Object value) throws IllegalArgumentException {
|
||||
if (value==null)
|
||||
throw new IllegalArgumentException("parameter "+ name+" is null");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package org.gcube.resources.discovery.client.queries.impl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
|
||||
|
||||
/**
|
||||
* A {@link SimpleQuery} over an XQuery template.
|
||||
* <p>
|
||||
* The template is defined as follows (cf. {@link #template}):
|
||||
* <p>
|
||||
* <code><ns/> for $resource in <range/><vars/> where <cond def="$result"/> return <result def="$result"/></code>
|
||||
* <p>
|
||||
*
|
||||
* where:
|
||||
*
|
||||
* <ul>
|
||||
* <li> {@link #range} stands for the path to the data ranged over by the <code>$result</code> variable. This parameter
|
||||
* is typically bound at query-creation time (cf. {@link #XQuery(Map)}.
|
||||
* <li> {@link #ns}, {@link #vars}, {@link #cond} and {@link #result} stand for, respectively, the declarations of namespace prefixes, the declarations of auxiliary variables,
|
||||
* the conditions, and the result expression of the query. These parameters should be bound through the {@link SimpleQuery} API.
|
||||
* </ul>
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class XQuery extends QueryTemplate implements SimpleQuery {
|
||||
|
||||
public static final String ns = "ns";
|
||||
public static final String vars = "vars";
|
||||
public static final String range = "range";
|
||||
public static final String cond = "cond";
|
||||
public static final String result = "result";
|
||||
|
||||
public static final String template = "<ns/> for $resource in <" + range + "/><vars/> where <" + cond + " " + DEFAULT
|
||||
+ "='$resource'/> return <" + result + " " + DEFAULT + "='$resource'/>";
|
||||
|
||||
public XQuery(Map<String, String> parameters) {// add static parameters
|
||||
|
||||
super(template, parameters);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* In the condition, <code>$resource</code> ranges over resources.
|
||||
*
|
||||
*/
|
||||
public XQuery addCondition(String condition) {
|
||||
|
||||
String newcond = "("+condition+")";
|
||||
|
||||
if (hasParameter(cond))
|
||||
appendParameter(cond," and "+newcond);
|
||||
else
|
||||
addParameter(cond,newcond);
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public XQuery addNamespace(String prefix, URI uri) {
|
||||
|
||||
String declaration = "declare namespace " + prefix + " = '" + uri + "';";
|
||||
|
||||
appendParameter(ns,declaration);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public XQuery addVariable(String variable, String range) {
|
||||
|
||||
String declaration = ", "+variable+" in "+range;
|
||||
|
||||
appendParameter(vars,declaration);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* In the expression, <code>$resource</code> ranges over resources.
|
||||
*/
|
||||
public XQuery setResult(String expression) {
|
||||
addParameter(result, expression);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue