diff --git a/dnet-core-components/pom.xml b/dnet-core-components/pom.xml index c147c77..50db0a8 100644 --- a/dnet-core-components/pom.xml +++ b/dnet-core-components/pom.xml @@ -16,10 +16,22 @@ org.apache.commons commons-lang3 + + commons-lang + commons-lang + commons-codec commons-codec + + commons-io + commons-io + + + com.mycila + xmltool + dom4j dom4j @@ -48,27 +60,136 @@ com.google.guava guava - - - org.mockito - mockito-core - test + com.google.code.gson + gson + junit junit test + + + log4j + log4j + + + com.sun.jmx + jmxri + + + com.sun.jdmk + jmxtools + + + + + + runcc + runcc + + + org.antlr + stringtemplate + + + jparsec + jparsec + + + apache + oro + + + + + org.quartz-scheduler + quartz + + + + org.apache.solr + solr-solrj + + + org.apache.lucene + lucene-queryparser + + + + org.mongodb + mongo-java-driver + + + joda-time + joda-time + + + org.z3950.zing + cql-java + + + + org.mockito + mockito-core + + + + + org.springframework + spring-beans + + + org.springframework + spring-web + org.springframework spring-test - false - commons-io - commons-io - test + org.springframework + spring-aop + + + org.springframework + spring-webmvc + + + org.springframework + spring-tx + + + + + + org.apache.cxf + cxf-core + + + org.apache.cxf + cxf-rt-bindings-soap + + + org.apache.cxf + cxf-rt-transports-http + + + org.apache.cxf + cxf-rt-frontend-jaxws + + + + javax.servlet + javax.servlet-api + + + + + org.apache.tomcat + tomcat-catalina diff --git a/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/APIDeprecatedException.java b/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/APIDeprecatedException.java new file mode 100644 index 0000000..6772560 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/APIDeprecatedException.java @@ -0,0 +1,10 @@ +package eu.dnetlib.common.rmi; + +public class APIDeprecatedException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = -5606373588445519515L; + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/BaseService.java b/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/BaseService.java new file mode 100644 index 0000000..6f94f72 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/BaseService.java @@ -0,0 +1,34 @@ +package eu.dnetlib.common.rmi; + +import javax.jws.WebMethod; +import javax.jws.WebParam; +import javax.jws.WebService; + +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface BaseService { + + /** + * All DRIVER services must implement method notify() in order to communicate with the IS_SN + * + * @param subsrciptionId + * @param topic + * @param isId + * @param message + */ + @WebMethod(operationName = "notify") + public void notify(@WebParam(name = "subscrId") String subscriptionId, + @WebParam(name = "topic") String topic, + @WebParam(name = "is_id") String isId, + @WebParam(name = "message") String message); + + /** + * Identifies the service's version. Version syntax: ${NAME}-${MAJOR}.${MINOR}.${MICRO}[-${LABEL}] + * + * @return the service's version + */ + @WebMethod(operationName = "identify") + public String identify(); + + public void start(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/DNetRestDocumentation.java b/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/DNetRestDocumentation.java new file mode 100644 index 0000000..5dc6adf --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/DNetRestDocumentation.java @@ -0,0 +1,16 @@ +package eu.dnetlib.common.rmi; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by claudio on 30/11/2016. + * to be used in REST controllers, and autodiscovered to build and publish their documentation + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DNetRestDocumentation { + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/RMIException.java b/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/RMIException.java new file mode 100644 index 0000000..0ec7ab4 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/RMIException.java @@ -0,0 +1,28 @@ +package eu.dnetlib.common.rmi; + +/** + * + * All RMI exception thrown from the service remote method invocation interfaces inherit this class + * + * @author marko + * + */ +abstract public class RMIException extends Exception { // NOPMD + + /** + * + */ + private static final long serialVersionUID = 428841258652765265L; + + public RMIException(final Throwable exception) { + super(exception); + } + + public RMIException(final String string) { + super(string); + } + + public RMIException(final String string, final Throwable exception) { + super(string, exception); + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/UnimplementedException.java b/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/UnimplementedException.java new file mode 100644 index 0000000..3d8b408 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/common/rmi/UnimplementedException.java @@ -0,0 +1,10 @@ +package eu.dnetlib.common.rmi; + +public class UnimplementedException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 6040968020696349497L; + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/conf/AbstractWebappContextProperty.java b/dnet-core-components/src/main/java/eu/dnetlib/conf/AbstractWebappContextProperty.java new file mode 100644 index 0000000..5188d8d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/conf/AbstractWebappContextProperty.java @@ -0,0 +1,60 @@ +package eu.dnetlib.conf; + +import javax.servlet.ServletContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.web.context.ServletContextAware; + +/** + * This spring bean detects the webapp context path, from the ServletContext. It requires servlet-api 2.5 (tomcat6 and + * jetty 6.x). + * + *

+ * Concrete subclasses will know what to do with this information + *

+ * + * @author marko + * + */ +public abstract class AbstractWebappContextProperty implements ServletContextAware { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(AbstractWebappContextProperty.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * web application context. + */ + protected String context; + + /** + * saved servlet context. + */ + protected ServletContext servletContext; + + @Override + public void setServletContext(final ServletContext servletContext) { + this.servletContext = servletContext; + + try { + context = servletContext.getContextPath().substring(1); + } catch (final NoSuchMethodError e) { + log.warn("cannot detect servlet context path. servlet-api 2.5 is required, falling back to property based configuration", e); + } + } + + public String getContext() { + return context; + } + + public void setContext(final String context) { + this.context = context; + } + + public ServletContext getServletContext() { + return servletContext; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/conf/PropertyFetcher.java b/dnet-core-components/src/main/java/eu/dnetlib/conf/PropertyFetcher.java new file mode 100644 index 0000000..25996a4 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/conf/PropertyFetcher.java @@ -0,0 +1,87 @@ +package eu.dnetlib.conf; + +import java.util.Map.Entry; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.PropertyResourceConfigurer; + +/** + * PropertyFletcher + * + * @author marko + * + */ +public class PropertyFetcher extends PropertyResourceConfigurer implements InitializingBean { + + private static final Log log = LogFactory.getLog(PropertyFetcher.class); // NOPMD by marko on 11/24/08 5:02 PM + + boolean unchangedHostname = false; + boolean unchangedPort = true; + + private Properties props; + + @Override + public void afterPropertiesSet() throws Exception { + this.props = mergeProperties(); + + // Convert the merged properties, if necessary. + convertProperties(props); + + log.debug("FOUND A container.hostname property " + props.getProperty("container.hostname")); + + if ("localhost".equals(props.getProperty("container.hostname"))) { + unchangedHostname = true; + } + if (props.getProperty("container.port") != null) { + log.debug("FOUND A container.port property " + props.getProperty("container.port")); + unchangedPort = false; + } + + if (log.isDebugEnabled()) { + log.debug("HOST unchanged? " + unchangedHostname); + log.debug("PORT unchanged? " + unchangedPort); + for (Entry e : props.entrySet()) { + log.debug("system property: " + e.getKey() + " --> " + e.getValue()); + } + } + } + + @Override + protected void processProperties(final ConfigurableListableBeanFactory arg0, final Properties props) throws BeansException { + + } + + public boolean isUnchangedHostname() { + return unchangedHostname; + } + + public void setUnchangedHostname(final boolean unchangedHostname) { + this.unchangedHostname = unchangedHostname; + } + + public boolean isUnchangedPort() { + return unchangedPort; + } + + public void setUnchangedPort(final boolean unchangedPort) { + this.unchangedPort = unchangedPort; + } + + public String getProperty(final String key) { + return props.getProperty(key); + } + + public Properties getProps() { + return props; + } + + public void setProps(final Properties props) { + this.props = props; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/conf/WebappContextPropertyLocationFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/conf/WebappContextPropertyLocationFactory.java new file mode 100644 index 0000000..35886cb --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/conf/WebappContextPropertyLocationFactory.java @@ -0,0 +1,66 @@ +package eu.dnetlib.conf; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.FactoryBean; + +/** + * This class wraps a list of property file paths and expands the @{webapp} placeholder with the name of the webapp taken from servlet-api + * 2.5. + * + * @author marko + * + */ +public class WebappContextPropertyLocationFactory extends AbstractWebappContextProperty implements FactoryBean> { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(WebappContextPropertyLocationFactory.class); // NOPMD by marko on 11/24/08 5:02 PM + + private List locations; + + @Override + public List getObject() throws Exception { + List expanded = new ArrayList(); + + for (String loc : locations) { + expanded.add(expand(loc).trim()); + } + + if (log.isInfoEnabled()) { + for (String location : expanded) { + log.info("Searching property file: " + location); + } + } + + return expanded; + } + + private String expand(final String loc) { + if (getContext() == null) return loc; + return loc.replace("@{webapp}", getContext()); + } + + @Override + public Class getObjectType() { + return locations.getClass(); + } + + @Override + public boolean isSingleton() { + return true; + } + + public List getLocations() { + return locations; + } + + public void setLocations(final List locations) { + this.locations = locations; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/conf/WebappContextProperyFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/conf/WebappContextProperyFactory.java new file mode 100644 index 0000000..d47e140 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/conf/WebappContextProperyFactory.java @@ -0,0 +1,146 @@ +package eu.dnetlib.conf; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Properties; + +import javax.servlet.ServletContext; + +import org.apache.catalina.Container; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.core.StandardEngine; +import org.apache.commons.lang.reflect.FieldUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.annotation.Required; + +/** + * This factory generates default properties based on the amount of information available from the servlet container. + * + * @author marko + * + */ +public class WebappContextProperyFactory extends AbstractWebappContextProperty implements FactoryBean { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(WebappContextProperyFactory.class); // NOPMD by marko on + // 11/24/08 5:02 PM + + /** + * If false, the ip address will be returned, otherwise a best effort attemp will be made to retrieve a meaningful host name. There is a + * risk that the hostname is obtained without domain and thus completely useless. + */ + private boolean resolveHostname = false; + + private PropertyFetcher propertyFetcher; + + @Override + public Properties getObject() throws Exception { + Properties props = new Properties(); + + if (getContext() == null) return props; + + props.setProperty("container.context", getContext()); + + log.debug("trying to autodetect port and hostame"); + + // if the user didn't customize, then autodetect, otherwise honour the + // user! + if (propertyFetcher.isUnchangedPort()) { + log.debug("PORT IS NOT OVERRIDDEN, autodetecting"); + int port = getPort(getServletContext()); + if (port > 0) { + props.setProperty("container.port", Integer.toString(port)); + } + } else { + log.debug("PORT IS OVERRIDDEN, NOT autodetecting"); + } + + if (propertyFetcher.isUnchangedHostname()) { + log.debug("HOST IS NOT OVERRIDDEN, autodetecting"); + String hostname = getHost(getServletContext()); + if (hostname != null) { + props.setProperty("container.hostname", hostname); + } + } else { + log.debug("HOST IS OVERRIDDEN, NOT autodetecting"); + } + + return props; + } + + @Override + public Class getObjectType() { + return Properties.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + private int getPort(final ServletContext servletContext) { + try { + return getContainerPort(servletContext); + } catch (Throwable e) { + log.warn("cannot obtain port from container...strange: I thought it would work both on jetty and tomcat7...)", e); + return 0; + } + } + + private int getContainerPort(final ServletContext servletContext) { + try { + Object o = FieldUtils.readField(servletContext, "context", true); + StandardContext sCtx = (StandardContext) FieldUtils.readField(o, "context", true); + Container container = sCtx; + + Container c = container.getParent(); + while ((c != null) && !(c instanceof StandardEngine)) { + c = c.getParent(); + } + + if (c != null) { + StandardEngine engine = (StandardEngine) c; + for (Connector connector : engine.getService().findConnectors()) { + if (connector.getPort() > 0) return connector.getPort(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + private String getHost(final ServletContext servletContext) { + try { + if (resolveHostname) return InetAddress.getLocalHost().getCanonicalHostName(); + else return InetAddress.getLocalHost().toString().split("/")[1]; + } catch (UnknownHostException e) { + log.warn("cannot obtain hostname from JVM", e); + } + + return null; + } + + public boolean isResolveHostname() { + return resolveHostname; + } + + public void setResolveHostname(final boolean resolveHostname) { + this.resolveHostname = resolveHostname; + } + + public PropertyFetcher getPropertyFetcher() { + return propertyFetcher; + } + + @Required + public void setPropertyFetcher(final PropertyFetcher propertyFetcher) { + this.propertyFetcher = propertyFetcher; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/information/collectionservice/rmi/CollectionService.java b/dnet-core-components/src/main/java/eu/dnetlib/data/information/collectionservice/rmi/CollectionService.java new file mode 100644 index 0000000..b720e52 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/information/collectionservice/rmi/CollectionService.java @@ -0,0 +1,28 @@ +package eu.dnetlib.data.information.collectionservice.rmi; + +import java.util.List; + +import javax.jws.WebParam; +import javax.jws.WebService; + +import eu.dnetlib.common.rmi.BaseService; + +/** + * The Collection Service is used to ... + * + * + */ + +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface CollectionService extends BaseService { + + public String getCollection(@WebParam(name = "collId") final String collId) throws CollectionServiceException; + + public List getCollections(@WebParam(name = "collIds") final List collIds) throws CollectionServiceException; + + public void updateCollection(@WebParam(name = "coll") final String coll) throws CollectionServiceException; + + public void deleteCollection(@WebParam(name = "collId") final String collId) throws CollectionServiceException; + + public String createCollection(@WebParam(name = "coll") final String coll) throws CollectionServiceException; +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/information/collectionservice/rmi/CollectionServiceException.java b/dnet-core-components/src/main/java/eu/dnetlib/data/information/collectionservice/rmi/CollectionServiceException.java new file mode 100644 index 0000000..9ae20f3 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/information/collectionservice/rmi/CollectionServiceException.java @@ -0,0 +1,28 @@ +package eu.dnetlib.data.information.collectionservice.rmi; + + +import javax.xml.ws.WebFault; + +import eu.dnetlib.common.rmi.RMIException; + +@WebFault +public class CollectionServiceException extends RMIException { + + /** + * + */ + private static final long serialVersionUID = 8094008463553904905L; + + public CollectionServiceException(Throwable e) { + super(e); + } + + public CollectionServiceException(String message, Throwable e) { + super(message, e); + } + + public CollectionServiceException(String message) { + super(message); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/information/publisher/rmi/PublisherService.java b/dnet-core-components/src/main/java/eu/dnetlib/data/information/publisher/rmi/PublisherService.java new file mode 100644 index 0000000..128a2ec --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/information/publisher/rmi/PublisherService.java @@ -0,0 +1,50 @@ +package eu.dnetlib.data.information.publisher.rmi; + +import java.util.List; + +import javax.jws.WebMethod; +import javax.jws.WebParam; +import javax.jws.WebService; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.common.rmi.BaseService; + +/** + * Publisher service. Provides access to metadata records and objects. + * + * @author marko + * + */ +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface PublisherService extends BaseService { + + /** + * Get a (metadata) resource by ID. + * + * @param id + * @param format + * @param layout + * @param interpretation + * @return + */ + @WebMethod + String getResourceById(@WebParam(name = "id") final String id, + @WebParam(name = "format") final String format, + @WebParam(name = "layout") final String layout, + @WebParam(name = "interpretation") final String interpretation); + + /** + * Get (metadata) resources by IDs. + * + * @param ids + * @param format + * @param layout + * @param interpretation + * @return + */ + @WebMethod + W3CEndpointReference getResourcesByIds(@WebParam(name = "ids") final List ids, + @WebParam(name = "format") final String format, + @WebParam(name = "layout") final String layout, + @WebParam(name = "interpretation") final String interpretation); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/mdstore/DocumentNotFoundException.java b/dnet-core-components/src/main/java/eu/dnetlib/data/mdstore/DocumentNotFoundException.java new file mode 100644 index 0000000..610f4a0 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/mdstore/DocumentNotFoundException.java @@ -0,0 +1,29 @@ +package eu.dnetlib.data.mdstore; + +/** + * Signals that a metadata record cannot be found in a given MDStore. + */ +public class DocumentNotFoundException extends MDStoreServiceException { + + /** + * + */ + private static final long serialVersionUID = 5188036989114250548L; + + public DocumentNotFoundException(final String s, final Throwable e) { + super(s, e); + } + + public DocumentNotFoundException(final String s) { + super(s); + } + + public DocumentNotFoundException(final Throwable e) { + super(e); + } + + public DocumentNotFoundException() { + super(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/mdstore/MDStoreService.java b/dnet-core-components/src/main/java/eu/dnetlib/data/mdstore/MDStoreService.java new file mode 100644 index 0000000..6e7de14 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/mdstore/MDStoreService.java @@ -0,0 +1,105 @@ +package eu.dnetlib.data.mdstore; + +import java.util.List; + +import javax.jws.WebMethod; +import javax.jws.WebParam; +import javax.jws.WebService; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.common.rmi.BaseService; + +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface MDStoreService extends BaseService { + + /** + * Identifies service and version. + * + * @return + */ + @Override + public String identify(); + + /** + * Returns ResultSet EPR for delivered mdstore records. + * + * @param mdId + * @param from + * @param until + * @param recordFilter + * REGEX on the metadata record + * @return ResultSet EPR + * @throws MDStoreServiceException + */ + public W3CEndpointReference deliverMDRecords(@WebParam(name = "mdId") final String mdId, + @WebParam(name = "from") final String from, + @WebParam(name = "until") final String until, + @WebParam(name = "recordsFilter") final String recordFilter) throws MDStoreServiceException; + + /** + * Deliver single record from selected mdstore. + * + * @param mdId + * @param recordId + * @return record + * @throws MDStoreServiceException + */ + public String deliverRecord(@WebParam(name = "mdId") final String mdId, @WebParam(name = "recordId") final String recordId) throws MDStoreServiceException, + DocumentNotFoundException; + + /** + * Returns list of all stored indices. + * + * @return list of all stored indices + */ + public List getListOfMDStores() throws MDStoreServiceException; + + public List listMDStores(@WebParam(name = "format") final String format, + @WebParam(name = "layout") final String layout, + @WebParam(name = "interpretation") final String interpretation) throws MDStoreServiceException; + + public W3CEndpointReference bulkDeliverMDRecords(@WebParam(name = "format") final String format, + @WebParam(name = "layout") final String layout, + @WebParam(name = "interpretation") final String interpretation) throws MDStoreServiceException; + + /** + * Store md records from a result set + * + * @param mdId + * @param rsId + * @param storingType + * @return returns true immediately. + * @throws MDStoreServiceException + */ + @Deprecated + public boolean storeMDRecordsFromRS(@WebParam(name = "mdId") final String mdId, + @WebParam(name = "rsId") final String rsId, + @WebParam(name = "storingType") final String storingType) throws MDStoreServiceException; + + /** + * Gets the size of the mdstore with the given identifier. + * + * @param mdId + * identifier of an mdstore + * @return the number of records in the store + */ + @WebMethod(operationName = "size") + public int size(@WebParam(name = "mdId") final String mdId) throws MDStoreServiceException; + + /** + * Gets the sum of records stored in all mdstore with the given format, layout , interpretation + * + * @param format + * format + * @param layout + * layout + * @param interpretation + * interpretation + * @return the total number of records in the mdstores of the given type + */ + @WebMethod(operationName = "sizeByFormat") + public int size(@WebParam(name = "format") final String format, + @WebParam(name = "layout") final String layout, + @WebParam(name = "interpretation") final String interpretation) throws MDStoreServiceException; + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/mdstore/MDStoreServiceException.java b/dnet-core-components/src/main/java/eu/dnetlib/data/mdstore/MDStoreServiceException.java new file mode 100644 index 0000000..73ab244 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/mdstore/MDStoreServiceException.java @@ -0,0 +1,32 @@ +package eu.dnetlib.data.mdstore; + +/** + * General mdstore service exception. + * @author claudio atzori + * @version 1.0.0 + * + */ +public class MDStoreServiceException extends Exception { + + /** + * + */ + private static final long serialVersionUID = -6772977735282310658L; + + public MDStoreServiceException(String s, Throwable e) { + super(s, e); + } + + public MDStoreServiceException(String s) { + super(s); + } + + public MDStoreServiceException(Throwable e) { + super(e); + } + + public MDStoreServiceException() { + super(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/BrowsingRow.java b/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/BrowsingRow.java new file mode 100644 index 0000000..3081d1d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/BrowsingRow.java @@ -0,0 +1,65 @@ +package eu.dnetlib.data.provision.index.rmi; + +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * serialization of the browsing result. + * + * facetFieldValue 1 + * + * facetFieldValue 1 + * + * + * + * @author claudio + * + */ +@XmlRootElement(namespace = "", name = "row") +@XmlAccessorType(XmlAccessType.FIELD) +public class BrowsingRow { + + @XmlElement(name = "groupresult", required = true) + private List groupresult; + + public BrowsingRow() {} + + public BrowsingRow(final List groupresult) { + this.groupresult = groupresult; + } + + /** + * adds a GroupResult. + * + * @param fieldName + * @param fieldValue + * @param count + */ + public void addBrowsingRow(final String fieldName, final String fieldValue, final int count) { + groupresult.add(new GroupResult(fieldName, fieldValue, count)); + } + + @Override + public boolean equals(final Object obj) { + + if (!(obj instanceof BrowsingRow)) return false; + + final BrowsingRow brws = (BrowsingRow) obj; + + return groupresult.equals(brws.getGroupResult()); + } + + public List getGroupResult() { + return groupresult; + } + + public void setGroupResult(final List groupresult) { + this.groupresult = groupresult; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/GroupResult.java b/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/GroupResult.java new file mode 100644 index 0000000..ee6dd3d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/GroupResult.java @@ -0,0 +1,76 @@ +package eu.dnetlib.data.provision.index.rmi; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +/** + *
+ * {@code
+ * 
+ * 	facetFieldValue
+ * 	1
+ * 
+ * }
+ * 
+ * + * @author claudio + * + */ +@XmlRootElement(namespace = "", name = "groupresult") +public class GroupResult { + + private String name; + + private String value; + + private int count; + + public GroupResult() {} + + /** + * Builds a groupResult. + * + * @param fieldName + * @param fieldValue + * @param count + */ + public GroupResult(final String name, final String value, final int count) { + this.name = name; + this.value = value; + this.count = count; + } + + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof GroupResult)) return false; + final GroupResult g = (GroupResult) obj; + if ((this.getCount() == g.getCount()) && this.getName().equals(g.getName()) && this.getValue().equals(g.getValue())) return true; + return false; + } + + @XmlAttribute(name = "field", required = true) + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + public int getCount() { + return count; + } + + public void setName(final String name) { + this.name = name; + } + + public void setValue(final String value) { + this.value = value; + } + + public void setCount(final int count) { + this.count = count; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/IndexService.java b/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/IndexService.java new file mode 100644 index 0000000..b518396 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/IndexService.java @@ -0,0 +1,27 @@ +package eu.dnetlib.data.provision.index.rmi; + +import java.util.List; + +import javax.jws.WebMethod; +import javax.jws.WebService; + +import eu.dnetlib.common.rmi.BaseService; + +/** + * Interface for the IndexService. + * + * @author alessia + * + */ +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface IndexService extends BaseService { + + /** + * Returns list of all stored indices. + * + * @return list of all stored indices + * @throws IndexServiceException + */ + @WebMethod(operationName = "getListOfIndices", action = "getListOfIndices") + public List getListOfIndices() throws IndexServiceException; +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/IndexServiceException.java b/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/IndexServiceException.java new file mode 100644 index 0000000..766a60d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/IndexServiceException.java @@ -0,0 +1,23 @@ +package eu.dnetlib.data.provision.index.rmi; + +public class IndexServiceException extends Exception { + + private static final long serialVersionUID = 8330264706967294512L; + + public IndexServiceException() { + super(); + } + + public IndexServiceException(String message, Throwable cause) { + super(message, cause); + } + + public IndexServiceException(String message) { + super(message); + } + + public IndexServiceException(Throwable cause) { + super(cause); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/protocols/IndexProtocols.java b/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/protocols/IndexProtocols.java new file mode 100644 index 0000000..f75fb87 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/provision/index/rmi/protocols/IndexProtocols.java @@ -0,0 +1,5 @@ +package eu.dnetlib.data.provision.index.rmi.protocols; + +public enum IndexProtocols { + SOLR, SOLR_CLOUD +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/utility/objectpackaging/rmi/ObjectPackagingException.java b/dnet-core-components/src/main/java/eu/dnetlib/data/utility/objectpackaging/rmi/ObjectPackagingException.java new file mode 100644 index 0000000..0ed1277 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/utility/objectpackaging/rmi/ObjectPackagingException.java @@ -0,0 +1,28 @@ +package eu.dnetlib.data.utility.objectpackaging.rmi; + +import javax.xml.ws.WebFault; + +import eu.dnetlib.common.rmi.RMIException; + +@WebFault +public class ObjectPackagingException extends RMIException { + + private static final long serialVersionUID = 3468254939586031822L; + + /** + * + */ + + public ObjectPackagingException(Throwable e) { + super(e); + } + + public ObjectPackagingException(String message, Throwable e) { + super(message, e); + } + + public ObjectPackagingException(String message) { + super(message); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/data/utility/objectpackaging/rmi/ObjectPackagingService.java b/dnet-core-components/src/main/java/eu/dnetlib/data/utility/objectpackaging/rmi/ObjectPackagingService.java new file mode 100644 index 0000000..4fd7ace --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/data/utility/objectpackaging/rmi/ObjectPackagingService.java @@ -0,0 +1,35 @@ +package eu.dnetlib.data.utility.objectpackaging.rmi; + +import java.util.List; + +import javax.jws.WebParam; +import javax.jws.WebService; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.common.rmi.BaseService; + +/** The Object Packaging Service is used to combine the records spread + * into one information package, namely an Object Record. + */ + + +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface ObjectPackagingService extends BaseService { + /** Return the EPR of the resultSet containing the generated packages + * + * @param eprs A list of EPRs used to access the input resultSets. ResultSets MUST be ordered using an order key identified by xpath_ID + * @param xpath_ID A valid xpath, used to access the ordered ID of the elements of the input resultSets. + * @return EPR of the generated resultset + */ + W3CEndpointReference generatePackages(@WebParam(name="eprs") List eprs, + @WebParam(name="xpath_ID") String xpath_ID) throws ObjectPackagingException; + + /** Return the EPR of the resultSet containing the unpackaged element + * + * @param epr The epr used to access the resultset that contains input packages, packages are xml record in this format: REC1REC2REC3 + * @return EPR of the generated resultset + */ + W3CEndpointReference splitPackages(@WebParam(name="epr") W3CEndpointReference epr) throws ObjectPackagingException; + + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/common/Stoppable.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/common/Stoppable.java new file mode 100644 index 0000000..99a3440 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/common/Stoppable.java @@ -0,0 +1,7 @@ +package eu.dnetlib.enabling.common; + +public interface Stoppable { + void stop(); + void resume(); + StoppableDetails getStopDetails(); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/common/StoppableDetails.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/common/StoppableDetails.java new file mode 100644 index 0000000..a342a4a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/common/StoppableDetails.java @@ -0,0 +1,45 @@ +package eu.dnetlib.enabling.common; + +public class StoppableDetails { + + public enum StopStatus { + RUNNING, STOPPED, STOPPING + } + + private String name; + private String message; + private StopStatus status; + + public StoppableDetails() {} + + public StoppableDetails(String name, String message, StopStatus status) { + this.name = name; + this.message = message; + this.status = status; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public StopStatus getStatus() { + return status; + } + + public void setStatus(StopStatus status) { + this.status = status; + } + +} \ No newline at end of file diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/dlm/rmi/DlmService.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/dlm/rmi/DlmService.java new file mode 100644 index 0000000..c7403f5 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/dlm/rmi/DlmService.java @@ -0,0 +1,18 @@ +package eu.dnetlib.enabling.dlm.rmi; + +import javax.jws.WebService; + +import eu.dnetlib.common.rmi.BaseService; + +/** + * Distributed lock manager. Currently is used mostly to start the underlying lock manager (e.g. zookeeper) and let + * client interface directly with it. + * + *

The DLM service profile contains the entry point of the underlying locking service.

+ * + * @author marko + * + */ +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface DlmService extends BaseService { +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/hcm/rmi/HostingContextManagerService.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/hcm/rmi/HostingContextManagerService.java new file mode 100644 index 0000000..fd03ebe --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/hcm/rmi/HostingContextManagerService.java @@ -0,0 +1,20 @@ +package eu.dnetlib.enabling.hcm.rmi; + +import javax.jws.WebService; + +import eu.dnetlib.common.rmi.BaseService; + +/** + * Like a HostingNodeManager, but any webapp (web context) can have its own. + *

+ * useful for dispatching notifications shared by all the services local to a single context. + *

+ * + * @author marko + * @author antonis + * + */ +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface HostingContextManagerService extends BaseService { + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/hnm/rmi/HostingNodeManagerService.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/hnm/rmi/HostingNodeManagerService.java new file mode 100644 index 0000000..e4d699f --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/hnm/rmi/HostingNodeManagerService.java @@ -0,0 +1,17 @@ +package eu.dnetlib.enabling.hnm.rmi; + +import javax.jws.WebParam; +import javax.jws.WebService; + +import eu.dnetlib.common.rmi.BaseService; + +/** + * The HostingNodeManager Service is used to ... + * + * + */ + +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface HostingNodeManagerService extends BaseService { + public String echo(@WebParam(name = "s") String s); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/lookup/rmi/ISLookUpDocumentNotFoundException.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/lookup/rmi/ISLookUpDocumentNotFoundException.java new file mode 100644 index 0000000..e58fceb --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/lookup/rmi/ISLookUpDocumentNotFoundException.java @@ -0,0 +1,47 @@ +package eu.dnetlib.enabling.is.lookup.rmi; + +import javax.xml.ws.WebFault; + + +/** + * Thrown when a given document is not found. + * + * @author marko + * + */ +@WebFault +public class ISLookUpDocumentNotFoundException extends ISLookUpException { + + /** + * exception chain + message. + * + * @param message message + * @param e + */ + public ISLookUpDocumentNotFoundException(String message, Throwable e) { + super(message, e); + } + + /** + * exception chain constructor. + * @param e + */ + public ISLookUpDocumentNotFoundException(Throwable e) { + super(e); + } + + /** + * exception message. + * + * @param message + */ + public ISLookUpDocumentNotFoundException(String message) { + super(message); + } + + /** + * + */ + private static final long serialVersionUID = 2295995755165801937L; + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/lookup/rmi/ISLookUpException.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/lookup/rmi/ISLookUpException.java new file mode 100644 index 0000000..07044ac --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/lookup/rmi/ISLookUpException.java @@ -0,0 +1,27 @@ +package eu.dnetlib.enabling.is.lookup.rmi; + +import javax.xml.ws.WebFault; + +import eu.dnetlib.common.rmi.RMIException; + +@WebFault +public class ISLookUpException extends RMIException { + + /** + * + */ + private static final long serialVersionUID = -5626136963653382533L; + + public ISLookUpException(Throwable e) { + super(e); + } + + public ISLookUpException(String message, Throwable e) { + super(message, e); + } + + public ISLookUpException(String message) { + super(message); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/lookup/rmi/ISLookUpService.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/lookup/rmi/ISLookUpService.java new file mode 100644 index 0000000..ff1ade4 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/lookup/rmi/ISLookUpService.java @@ -0,0 +1,53 @@ +package eu.dnetlib.enabling.is.lookup.rmi; + +import java.util.List; + +import javax.jws.WebParam; +import javax.jws.WebService; + +import eu.dnetlib.common.rmi.BaseService; + +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface ISLookUpService extends BaseService { + + Boolean flushCachedResultSets(); + + @Deprecated + String getCollection(@WebParam(name = "profId") String profId, @WebParam(name = "format") String format) throws ISLookUpException; + + String retrieveCollection(@WebParam(name = "profId") String profId) throws ISLookUpException; + + String getResourceProfile(@WebParam(name = "profId") String profId) throws ISLookUpException, ISLookUpDocumentNotFoundException; + + String getResourceProfileByQuery(@WebParam(name = "XQuery") String XQuery) throws ISLookUpException, ISLookUpDocumentNotFoundException; + + String getResourceQoSParams(@WebParam(name = "id") String id) throws ISLookUpException; + + String getResourceTypeSchema(@WebParam(name = "resourceType") String resourceType) throws ISLookUpException, ISLookUpDocumentNotFoundException; + + List listCollections( + @WebParam(name = "format") String format, + @WebParam(name = "idfather") String idfather, + @WebParam(name = "owner") String owner) throws ISLookUpException; + + @Deprecated + List listDHNIDs() throws ISLookUpException; + + List listResourceTypes() throws ISLookUpException; + + @Deprecated + List listServiceIDs(@WebParam(name = "serviceType") String serviceType) throws ISLookUpException; + + @Deprecated + List listServiceTypes() throws ISLookUpException; + + /** + * Like searchProfile(), but bypassing the resultset. Useful for short xquery results. + * + * @param xquery xquery to be executed + * @return list of strings (never null) + * @throws ISLookUpException could happen + */ + List quickSearchProfile(@WebParam(name = "XQuery") String xquery) throws ISLookUpException; + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/ISRegistryDocumentNotFoundException.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/ISRegistryDocumentNotFoundException.java new file mode 100644 index 0000000..3082a24 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/ISRegistryDocumentNotFoundException.java @@ -0,0 +1,27 @@ +package eu.dnetlib.enabling.is.registry; + +import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException; + +public class ISRegistryDocumentNotFoundException extends ISRegistryException { + + /** + * + */ + private static final long serialVersionUID = -1304948213334188538L; + + public ISRegistryDocumentNotFoundException(String string, Throwable e) { + super(string, e); + // TODO Auto-generated constructor stub + } + + public ISRegistryDocumentNotFoundException(String string) { + super(string); + // TODO Auto-generated constructor stub + } + + public ISRegistryDocumentNotFoundException(Throwable e) { + super(e); + // TODO Auto-generated constructor stub + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/rmi/ISRegistryException.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/rmi/ISRegistryException.java new file mode 100644 index 0000000..a077b8a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/rmi/ISRegistryException.java @@ -0,0 +1,24 @@ +package eu.dnetlib.enabling.is.registry.rmi; + +import eu.dnetlib.common.rmi.RMIException; + +public class ISRegistryException extends RMIException { + + /** + * + */ + private static final long serialVersionUID = -3347405941287624771L; + + public ISRegistryException(Throwable e) { + super(e); + } + + public ISRegistryException(String string) { + super(string); + } + + public ISRegistryException(String string, Throwable e) { + super(string, e); + } + +} \ No newline at end of file diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/rmi/ISRegistryService.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/rmi/ISRegistryService.java new file mode 100644 index 0000000..b502989 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/rmi/ISRegistryService.java @@ -0,0 +1,75 @@ +package eu.dnetlib.enabling.is.registry.rmi; + +import java.util.List; + +import javax.jws.WebService; + +import eu.dnetlib.common.rmi.BaseService; +import eu.dnetlib.enabling.is.registry.ISRegistryDocumentNotFoundException; + +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface ISRegistryService extends BaseService { + + boolean addOrUpdateResourceType(String resourceType, String resourceSchema) throws ISRegistryException; + + boolean addResourceType(String resourceType, String resourceSchema) throws ISRegistryException; + + boolean deleteProfile(String profId) throws ISRegistryException, ISRegistryDocumentNotFoundException; + + @Deprecated + boolean deleteProfiles(List arrayprofId) throws ISRegistryException; + + /** + * @param resourceType + * @param hierarchical + * remove subscription topics + * @return + * @throws ISRegistryException + */ + boolean deleteResourceType(String resourceType, Boolean hierarchical) throws ISRegistryException; + + boolean executeXUpdate(String XQuery) throws ISRegistryException; + + String insertProfileForValidation(String resourceType, String resourceProfile) throws ISRegistryException; + + String invalidateProfile(String profId) throws ISRegistryException; + + boolean refreshProfile(String profId, String resourceType) throws ISRegistryException; + + /** + * register a XML Profile. + * + * @param resourceProfile + * xml profile + * @return profile id + * @throws ISRegistryException + */ + String registerProfile(String resourceProfile) throws ISRegistryException; + + String registerSecureProfile(String resourceProfId, String secureProfId) throws ISRegistryException; + + boolean updateProfile(String profId, String resourceProfile, String resourceType) throws ISRegistryException; + + @Deprecated + String updateProfileDHN(String resourceProfile) throws ISRegistryException; + + boolean addProfileNode(String profId, String xpath, String node) throws ISRegistryException; + + boolean updateProfileNode(String profId, String xpath, String node) throws ISRegistryException; + + boolean removeProfileNode(String profId, String nodeId) throws ISRegistryException; + + @Deprecated + boolean updateRegionDescription(String profId, String resourceProfile) throws ISRegistryException; + + String validateProfile(String profId) throws ISRegistryException; + + @Deprecated + List validateProfiles(List profIds) throws ISRegistryException; + + void addBlackBoardMessage(String profId, String messageId, String message) throws ISRegistryException; + + void replyBlackBoardMessage(String profId, String message) throws ISRegistryException; + + void deleteBlackBoardMessage(String profId, String messageId) throws ISRegistryException; +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/schema/OpaqueResourceValidator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/schema/OpaqueResourceValidator.java new file mode 100644 index 0000000..540f171 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/schema/OpaqueResourceValidator.java @@ -0,0 +1,19 @@ +package eu.dnetlib.enabling.is.registry.schema; + +import eu.dnetlib.enabling.tools.OpaqueResource; + +/** + * validates the conformity of a resource to a give resource type. + * + * @author marko + * + */ +public interface OpaqueResourceValidator { + /** + * check if the given resource is valid according to it's schema. + * + * @param resource opaque resource + * @throws ValidationException thrown if the validation fails, along with a description of the cause. + */ + void validate(OpaqueResource resource) throws ValidationException; +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/schema/ValidationException.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/schema/ValidationException.java new file mode 100644 index 0000000..4f750db --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/registry/schema/ValidationException.java @@ -0,0 +1,35 @@ +package eu.dnetlib.enabling.is.registry.schema; + + +/** + * encapsulates a schema validation exception. + * + * @author marko + * + */ +public class ValidationException extends Exception { + + /** + * version. + */ + private static final long serialVersionUID = -6886927707534508655L; + + /** + * construct a validation exception based upon an encapsulated cause. + * @param cause cause + */ + public ValidationException(final Throwable cause) { + super(cause); + } + + /** + * construct a validation exception with a message. + * + * @param message message + */ + public ValidationException(final String message) { + super(message); + } + + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/sn/rmi/ISSNException.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/sn/rmi/ISSNException.java new file mode 100644 index 0000000..ac878c6 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/sn/rmi/ISSNException.java @@ -0,0 +1,24 @@ +package eu.dnetlib.enabling.is.sn.rmi; + +import eu.dnetlib.common.rmi.RMIException; + +public class ISSNException extends RMIException { + + /** + * + */ + private static final long serialVersionUID = -7384073901457430004L; + + public ISSNException(final Throwable e) { + super(e); + } + + public ISSNException(final String message) { + super(message); + } + + public ISSNException(final String message, final Throwable e) { + super(message, e); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/sn/rmi/ISSNService.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/sn/rmi/ISSNService.java new file mode 100644 index 0000000..98999b5 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/sn/rmi/ISSNService.java @@ -0,0 +1,129 @@ +package eu.dnetlib.enabling.is.sn.rmi; + +import java.util.List; + +import javax.jws.WebParam; +import javax.jws.WebService; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.common.rmi.BaseService; + +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface ISSNService extends BaseService { + + /** + * fossil. + * + * @param topic + * @return + * @throws ISSNException + */ + String getCurrentMessage(@WebParam(name = "topic") String topic) throws ISSNException; + + /** + * puts a subcription in a paused state. paused subscription are not notified even when triggered. + * + * @param subscrId + * subscription identifier + * @return returns false if the subscription is already paused. + * @throws ISSNException + * may happen + */ + boolean pauseSubscription(@WebParam(name = "subscrId") String subscrId) throws ISSNException; + + /** + * Used to renew the subscription before it expires. + * + *

+ * In practice it resets the ttl to another value, so it can be used to reset a infinte ttl subscription to a finite + * value. + *

+ * + * @param subscrId + * subscription id + * @param terminationTime + * new ttl (from now), or 0 (infinite) + * @return true if successful + * @throws ISSNException + * may happen + */ + boolean renew(@WebParam(name = "subscrId") String subscrId, @WebParam(name = "terminationTime") int terminationTime) throws ISSNException; + + /** + * resumes a paused subscription. + * + * @param subscrId + * subscription id + * @return true if resumed. false if it was not paused. + * @throws ISSNException + * may happen + */ + boolean resumeSubscription(@WebParam(name = "subscrId") String subscrId) throws ISSNException; + + /** + * @param consumerReference + * epr to be called when the notification is triggered + * @param topicExpression + * topic expression to register + * @param initialTerminationTime + * ttl in seconds (0 = infinite) + * @return subscription id + * @throws ISSNException + * may happen + */ + String subscribe( + @WebParam(name = "consumerReference") W3CEndpointReference consumerReference, + @WebParam(name = "topicExpression") String topicExpression, + @WebParam(name = "initialTerminationTime") int initialTerminationTime) throws ISSNException, SubscriptionRequestRejectedException; + + boolean unsubscribe(@WebParam(name = "subscrId") String subscrId) throws ISSNException; + + /** + * fossil. + * + * @param resourceType + * @param profileId + * @param profile + * @return + * @throws ISSNException + */ + boolean actionCreatePerformed( + @WebParam(name = "resourceType") String resourceType, + @WebParam(name = "profileId") String profileId, + @WebParam(name = "profile") String profile) throws ISSNException; + + /** + * fossil. + * + * @param resourceType + * @param profileId + * @param profileBefore + * @param profileAfter + * @return + * @throws ISSNException + */ + boolean actionUpdatePerformed( + @WebParam(name = "resourceType") String resourceType, + @WebParam(name = "profileId") String profileId, + @WebParam(name = "profileBefore") String profileBefore, + @WebParam(name = "profileAfter") String profileAfter) throws ISSNException; + + /** + * fossil. + * + * @param resourceType + * @param profileId + * @return + * @throws ISSNException + */ + boolean actionDeletePerformed(@WebParam(name = "resourceType") String resourceType, @WebParam(name = "profileId") String profileId) + throws ISSNException; + + /** + * list all subscriptions. Mostly for debug reasons. + * + * @return list of subscription ids. + */ + List listSubscriptions(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/sn/rmi/SubscriptionRequestRejectedException.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/sn/rmi/SubscriptionRequestRejectedException.java new file mode 100644 index 0000000..bd23923 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/sn/rmi/SubscriptionRequestRejectedException.java @@ -0,0 +1,21 @@ +package eu.dnetlib.enabling.is.sn.rmi; + +/** + * Thrown when a subscription request is rejected. + * + * @author claudio + * + */ +public class SubscriptionRequestRejectedException extends ISSNException { + + /** + * + */ + private static final long serialVersionUID = 263095606953662098L; + + public SubscriptionRequestRejectedException(String message) { + super(message); + // TODO Auto-generated constructor stub + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/AbstractContentInitializer.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/AbstractContentInitializer.java new file mode 100644 index 0000000..db189e6 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/AbstractContentInitializer.java @@ -0,0 +1,135 @@ +package eu.dnetlib.enabling.is.store; + +import java.io.IOException; +import java.io.StringWriter; +import java.net.URL; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPathExpressionException; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Required; +import org.xml.sax.SAXException; + +import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException; +import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService; +import eu.dnetlib.enabling.locators.UniqueServiceLocator; +import eu.dnetlib.enabling.tools.StreamOpaqueResource; + +/** + * Abstract resource loading code. + * + * @author marko, michele + * + */ +public class AbstractContentInitializer { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(AbstractContentInitializer.class); // NOPMD by marko on 11/24/08 5:02 PM + /** + * service locator. + */ + private UniqueServiceLocator serviceLocator; + /** + * helper class used to bypass the registry and import resources as-is from a backup dump. + */ + private BulkResourceImporter bulkImporter; + + private int timeToSleep; + + /** + * register a schema from a local resource. + * + * @param url + * url + * @throws IOException + * happens + * @throws ISRegistryException + * could happen + */ + protected void registerSchema(final URL url) throws IOException, ISRegistryException { + final String resourceType = FilenameUtils.getBaseName(url.getPath()); + log.debug("registering schema: " + resourceType); + + final StringWriter writer = new StringWriter(); + IOUtils.copy(url.openStream(), writer); + ISRegistryService service = null; + while (service == null) { + try { + service = serviceLocator.getService(ISRegistryService.class, true); + service.addResourceType(resourceType, writer.getBuffer().toString()); + log.info("The is registry service is ready "); + } catch (Exception e) { + log.fatal("The is registry service is not ready ", e); + try { + Thread.sleep(timeToSleep); + } catch (InterruptedException e1) { + log.error(e1); + } + } + } + + } + + /** + * register a profile from a local resource. + * + * @param url + * url + * @throws IOException + * could happen + * @throws ISRegistryException + * could happen + * @throws ParserConfigurationException + * could happen + * @throws SAXException + * could happen + * @throws XPathExpressionException + * could happen + */ + protected void registerProfile(final URL url) throws IOException, ISRegistryException, XPathExpressionException, SAXException, ParserConfigurationException { + log.debug("registering profile: " + url); + + bulkImporter.importResource(new StreamOpaqueResource(url.openStream())); + } + + @Required + public void setBulkImporter(final BulkResourceImporter bulkImporter) { + this.bulkImporter = bulkImporter; + } + + public BulkResourceImporter getBulkImporter() { + return bulkImporter; + } + + /** + * @return the timeToSleep + */ + public int getTimeToSleep() { + return timeToSleep; + } + + /** + * @param timeToSleep + * the timeToSleep to set + */ + @Required + public void setTimeToSleep(final int timeToSleep) { + this.timeToSleep = timeToSleep; + } + + public UniqueServiceLocator getServiceLocator() { + return serviceLocator; + } + + @Required + public void setServiceLocator(final UniqueServiceLocator serviceLocator) { + this.serviceLocator = serviceLocator; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/BulkResourceImporter.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/BulkResourceImporter.java new file mode 100644 index 0000000..9304a29 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/BulkResourceImporter.java @@ -0,0 +1,115 @@ +package eu.dnetlib.enabling.is.store; + +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException; +import eu.dnetlib.enabling.is.registry.schema.OpaqueResourceValidator; +import eu.dnetlib.enabling.is.registry.schema.ValidationException; +import eu.dnetlib.enabling.is.store.rmi.ISStoreException; +import eu.dnetlib.enabling.is.store.rmi.ISStoreService; +import eu.dnetlib.enabling.locators.UniqueServiceLocator; +import eu.dnetlib.enabling.tools.OpaqueResource; +import eu.dnetlib.enabling.tools.XQueryUtils; + +/** + * This class implements a bulk resource import, i.e. importing stuff straight into the store, bypassing checks and policies imposed by the + * registry service. + * + * TODO: move from registry based to store based. + * + * @author marko + * + */ +public class BulkResourceImporter { + + /** + * xquery utils, needed to map resources with the xmldb collection names. + */ + private XQueryUtils xqueryUtils; + + /** + * service locator. + */ + private UniqueServiceLocator serviceLocator; + + /** + * resource validator. + */ + private OpaqueResourceValidator resourceValidator; + + /** + * set to false to skip validation. + */ + private boolean validating = true; + + /** + * bulk loading enabled. + */ + private boolean enabled = true; + + /** + * register a resource bypassing the checks. + * + * @param resource + * a resource + * @throws ISRegistryException + * could happen + */ + public void importResource(final OpaqueResource resource) throws ISRegistryException { + try { + if (validating) { + resourceValidator.validate(resource); + } + serviceLocator.getService(ISStoreService.class, true).insertXML(xqueryUtils.getFileName(resource), xqueryUtils.getCollectionAbsPath(resource), + resource.asString()); + } catch (final ISStoreException e) { + throw new ISRegistryException(e); + } catch (final ValidationException e) { + throw new ISRegistryException(e); + } + } + + public XQueryUtils getXqueryUtils() { + return xqueryUtils; + } + + @Required + public void setXqueryUtils(final XQueryUtils xqueryUtils) { + this.xqueryUtils = xqueryUtils; + } + + @Required + public void setResourceValidator(final OpaqueResourceValidator resourceValidator) { + this.resourceValidator = resourceValidator; + } + + public OpaqueResourceValidator getResourceValidator() { + return resourceValidator; + } + + public void setValidating(final boolean validating) { + this.validating = validating; + } + + public boolean isValidating() { + return validating; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(final boolean enabled) { + this.enabled = enabled; + } + + public UniqueServiceLocator getServiceLocator() { + return serviceLocator; + } + + @Required + public void setServiceLocator(final UniqueServiceLocator serviceLocator) { + this.serviceLocator = serviceLocator; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/rmi/ISStoreException.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/rmi/ISStoreException.java new file mode 100644 index 0000000..96c7721 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/rmi/ISStoreException.java @@ -0,0 +1,24 @@ +package eu.dnetlib.enabling.is.store.rmi; + +import eu.dnetlib.common.rmi.RMIException; + +public class ISStoreException extends RMIException { + + /** + * + */ + private static final long serialVersionUID = 8683126829156096420L; + + public ISStoreException(Throwable e) { + super(e); + } + + public ISStoreException(String message, Throwable e) { + super(message, e); + } + + public ISStoreException(String message) { + super(message); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/rmi/ISStoreService.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/rmi/ISStoreService.java new file mode 100644 index 0000000..4a7282a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/is/store/rmi/ISStoreService.java @@ -0,0 +1,44 @@ +package eu.dnetlib.enabling.is.store.rmi; + +import java.util.List; + +import javax.jws.WebParam; +import javax.jws.WebService; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.common.rmi.BaseService; + +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface ISStoreService extends BaseService { + + boolean createFileColl(@WebParam(name = "fileColl") String fileColl) throws ISStoreException; + + boolean deleteFileColl(@WebParam(name = "fileColl") String fileColl) throws ISStoreException; + + boolean deleteXML(@WebParam(name = "fileName") String fileName, @WebParam(name = "fileColl") String fileColl) throws ISStoreException; + + boolean executeXUpdate(@WebParam(name = "query") String query) throws ISStoreException; + + List getFileColls() throws ISStoreException; + + List getFileNames(@WebParam(name = "fileColl") String fileColl) throws ISStoreException; + + String getXML(@WebParam(name = "fileName") String fileName, @WebParam(name = "fileColl") String fileColl) throws ISStoreException; + + String getXMLbyQuery(@WebParam(name = "query") String query) throws ISStoreException; + + boolean insertXML(@WebParam(name = "fileName") String fileName, @WebParam(name = "fileColl") String fileColl, @WebParam(name = "file") String file) + throws ISStoreException; + + boolean reindex(); + + List quickSearchXML(@WebParam(name = "query") String query) throws ISStoreException; + + boolean sync(); + + boolean updateXML(@WebParam(name = "fileName") String fileName, @WebParam(name = "fileColl") String fileColl, @WebParam(name = "file") String file) + throws ISStoreException; + + String backup() throws ISStoreException; + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/DefaultUniqueServiceLocator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/DefaultUniqueServiceLocator.java new file mode 100644 index 0000000..39b88ea --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/DefaultUniqueServiceLocator.java @@ -0,0 +1,264 @@ +package eu.dnetlib.enabling.locators; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.*; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import eu.dnetlib.common.rmi.BaseService; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.tools.registration.ServiceNameResolver; +import eu.dnetlib.enabling.tools.registration.ValidatingServiceRegistrationManagerImpl; +import eu.dnetlib.miscutils.collections.EnsureCollection; +import eu.dnetlib.soap.cxf.StandaloneCxfEndpointReferenceBuilder; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Required; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +public class DefaultUniqueServiceLocator implements UniqueServiceLocator, ApplicationContextAware { + + private ApplicationContext appContext; + + private Comparator defaultComparator; // = new + // PreferLocalRunningInstanceComparator(); + + /** + * An instance of isLookupService (local or stub) + */ + @Autowired + private ISLookUpService isLookupService; + + @Autowired + private ServiceNameResolver serviceNameResolver; + + /** + * XML Parser + */ + private SAXReader reader = new SAXReader(); + + /** + * build epr. + */ + @Autowired + private StandaloneCxfEndpointReferenceBuilder eprBuilder; + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(DefaultUniqueServiceLocator.class); + + @Override + public T getService(final Class clazz) { + return getService(clazz, true); + } + + @Override + public T getService(final Class clazz, final Comparator comparator) { + final String serviceName = serviceNameResolver.getName(clazz); + return findRunningInstances(serviceName, comparator).get(0).obtainClient(clazz, eprBuilder); + } + + @Override + public T getService(final Class clazz, final String profileId) { + final String profile = obtainServiceProfile(profileId); + + try { + return obtainRunningInstance(profile, obtainLocalServices()).obtainClient(clazz, eprBuilder); + } catch (Exception e) { + log.error("cannot instantiate service from id: " + profileId, e); + throw new IllegalStateException("cannot instantiate service from id: " + profileId, e); + } + } + + @Override + public T getService(final Class clazz, final boolean local) { + if (clazz.isInstance(isLookupService)) return clazz.cast(isLookupService); + + if (local) { + try { + final Map beans = appContext.getBeansOfType(clazz); + if ((beans != null) && !beans.isEmpty()) return beans.values().iterator().next(); + } catch (Throwable e) { + log.warn("No beans found in context, class " + clazz); + } + } + + return getService(clazz, defaultComparator); + } + + @Override + public String getServiceId(final Class clazz) { + return getServiceId(clazz, defaultComparator); + } + + @Override + public String getServiceId(final Class clazz, final Comparator comparator) { + return findRunningInstances(serviceNameResolver.getName(clazz), comparator).get(0).getServiceId(); + } + + @Override + public String getServiceId(final Class clazz, final String profileId) { + final String profile = obtainServiceProfile(profileId); + final ServiceRunningInstance instance = obtainRunningInstance(profile, obtainLocalServices()); + return instance.getServiceId(); + } + + @Override + public Set getAllServices(final Class clazz) { + final Set res = Sets.newHashSet(); + for (ServiceRunningInstance instance : findRunningInstances(serviceNameResolver.getName(clazz), null)) { + res.add(instance.obtainClient(clazz, eprBuilder)); + } + return res; + } + + @Override + public Set getAllServiceIds(final Class clazz) { + final Set res = Sets.newHashSet(); + for (ServiceRunningInstance instance : findRunningInstances(serviceNameResolver.getName(clazz), null)) { + res.add(instance.getServiceId()); + } + return res; + } + + private synchronized ServiceRunningInstance obtainRunningInstance(final String profile, final Map locals) { + try { + final Document doc = reader.read(new StringReader(profile)); + final String url = doc.valueOf("//PROTOCOL[@name = 'SOAP']/@address"); + final String id = doc.valueOf("//RESOURCE_IDENTIFIER/@value"); + final Map props = Maps.newHashMap(); + final BaseService local = locals.containsKey(id) ? locals.get(id) : null; + final int usedDiskpace = NumberUtils.toInt(doc.valueOf("//USED_DISKSPACE"), 0); + final int handledDatastructures = NumberUtils.toInt(doc.valueOf("//HANDLED_DATASTRUCTURE"), 0);; + + for (Object o : doc.selectNodes("//SERVICE_PROPERTIES/PROPERTY")) { + final Element p = (Element) o; + props.put(p.valueOf("@key"), p.valueOf("@value")); + } + + return new ServiceRunningInstance(id, url, local, usedDiskpace, handledDatastructures, props); + } catch (DocumentException e) { + log.error("Error parsing profile: " + profile, e); + throw new RuntimeException("Error parsing profile: " + profile, e); + } + } + + private List findRunningInstances(final String serviceName, final Comparator comparator) { + final List list = findRegisteredServices(serviceName); + + if (list.isEmpty()) { + log.error("Service not found, name: " + serviceName); + throw new RuntimeException("Service not found, name: " + serviceName); + } + + if (comparator != null) { + Collections.sort(list, comparator); + } + + return list; + } + + private List findRegisteredServices(final String serviceName) { + log.debug("searching for service: " + serviceName); + + final String xquery = "for $x in collection('/db/DRIVER/ServiceResources/" + serviceName + "ResourceType') return $x"; + log.debug(xquery); + + try { + final List services = isLookupService.quickSearchProfile(xquery); + final List instances = Lists.newArrayList(); + final Map locals = obtainLocalServices(); + + for (final String source : EnsureCollection.list(services)) { + final ServiceRunningInstance instance = obtainRunningInstance(source, locals); + instances.add(instance); + } + return instances; + } catch (final Exception e) { + throw new IllegalStateException("cannot locate service " + serviceName, e); + } + } + + private Map obtainLocalServices() { + final Map locals = Maps.newHashMap(); + + for (ValidatingServiceRegistrationManagerImpl r : appContext.getBeansOfType(ValidatingServiceRegistrationManagerImpl.class).values()) { + if (r.getService() instanceof BaseService) { + if (!StringUtils.isBlank(r.getProfileId())) { + final BaseService baseService = (BaseService) r.getService(); + if (baseService != null) { + locals.put(r.getProfileId(), baseService); + log.debug(" -> Service: " + r.getService().getClass().getName() + " has id " + r.getServiceProfile().getResourceId()); + } + } + } + } + return locals; + } + + private String obtainServiceProfile(final String profileId) { + final StringWriter sw = new StringWriter(); + sw.append("let $uri:=/RESOURCE_PROFILE/HEADER[./RESOURCE_IDENTIFIER/@value='"); + sw.append(profileId); + sw.append("']/RESOURCE_URI/@value/string()"); + sw.append("\n\n"); + sw.append("for $x in collection('/db/DRIVER/ServiceResources')"); + sw.append("\n"); + sw.append("where $x/RESOURCE_PROFILE/HEADER/RESOURCE_URI/@value = $uri"); + sw.append("\n"); + sw.append("return $x"); + + final String xq = sw.toString(); + + try { + return isLookupService.getResourceProfileByQuery(xq); + } catch (ISLookUpException e) { + log.error("cannot locate service using query: " + xq, e); + throw new IllegalStateException("cannot locate service using query: " + xq, e); + } + } + + @Override + public void setApplicationContext(final ApplicationContext appContext) throws BeansException { + this.appContext = appContext; + } + + public Comparator getDefaultComparator() { + return defaultComparator; + } + + @Required + public void setDefaultComparator(final Comparator defaultComparator) { + this.defaultComparator = defaultComparator; + } + + public ISLookUpService getIsLookupService() { + return isLookupService; + } + + public void setIsLookupService(final ISLookUpService isLookupService) { + this.isLookupService = isLookupService; + } + + public ServiceNameResolver getServiceNameResolver() { + return serviceNameResolver; + } + + public void setServiceNameResolver(final ServiceNameResolver serviceNameResolver) { + this.serviceNameResolver = serviceNameResolver; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/ServiceRunningInstance.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/ServiceRunningInstance.java new file mode 100644 index 0000000..eb510ca --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/ServiceRunningInstance.java @@ -0,0 +1,102 @@ +package eu.dnetlib.enabling.locators; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.ws.WebServiceFeature; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.common.rmi.BaseService; +import eu.dnetlib.soap.cxf.StandaloneCxfEndpointReferenceBuilder; + +/** + * This bean packages the minimum information for describing a service running instance. + */ +public class ServiceRunningInstance { + + private String serviceId; + private String url; + private BaseService localService; + private int usedDiskSpace = 0; + private int handledDatastructures = 0; + private Map serviceProperties = new HashMap(); + + public ServiceRunningInstance() {} + + public ServiceRunningInstance(final String serviceId, final String url) { + this.serviceId = serviceId; + this.url = url; + } + + public ServiceRunningInstance(final String serviceId, final String url, final BaseService localService, final int usedDiskSpace, + final int handledDatastructures, final Map serviceProperties) { + this.serviceId = serviceId; + this.url = url; + this.localService = localService; + this.usedDiskSpace = usedDiskSpace; + this.handledDatastructures = handledDatastructures; + this.serviceProperties = serviceProperties; + } + + public boolean isLocal() { + return localService != null; + } + + synchronized public T obtainClient(final Class clazz, final StandaloneCxfEndpointReferenceBuilder eprBuilder) { + if (isLocal() && clazz.isInstance(localService)) { + return clazz.cast(localService); + } else { + final W3CEndpointReference epr = eprBuilder.getEndpointReference(url, null, null, url + "?wsdl", null, null); + return epr.getPort(clazz, new WebServiceFeature[] {}); + } + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(final String serviceId) { + this.serviceId = serviceId; + } + + public String getUrl() { + return url; + } + + public void setUrl(final String url) { + this.url = url; + } + + public BaseService getLocalService() { + return localService; + } + + public void setLocalService(final BaseService localService) { + this.localService = localService; + } + + public Map getServiceProperties() { + return serviceProperties; + } + + public void setServiceProperties(final Map serviceProperties) { + this.serviceProperties = serviceProperties; + } + + public int getUsedDiskSpace() { + return usedDiskSpace; + } + + public void setUsedDiskSpace(final int usedDiskSpace) { + this.usedDiskSpace = usedDiskSpace; + } + + public int getHandledDatastructures() { + return handledDatastructures; + } + + public void setHandledDatastructures(final int handledDatastructures) { + this.handledDatastructures = handledDatastructures; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/UniqueServiceLocator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/UniqueServiceLocator.java new file mode 100644 index 0000000..985148d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/UniqueServiceLocator.java @@ -0,0 +1,20 @@ +package eu.dnetlib.enabling.locators; + +import java.util.Comparator; +import java.util.Set; + +import eu.dnetlib.common.rmi.BaseService; + +public interface UniqueServiceLocator { + T getService(Class clazz); + T getService(Class clazz, Comparator comparator); + T getService(Class clazz, String profileId); + T getService(Class clazz, boolean local); + + String getServiceId(Class clazz); + String getServiceId(Class clazz, Comparator comparator); + String getServiceId(Class clazz, String profileId); + + Set getAllServices(Class clazz); + Set getAllServiceIds(Class clazz); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/comparators/DiskSpaceComparator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/comparators/DiskSpaceComparator.java new file mode 100644 index 0000000..f5962d5 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/comparators/DiskSpaceComparator.java @@ -0,0 +1,14 @@ +package eu.dnetlib.enabling.locators.comparators; + +import java.util.Comparator; + +import eu.dnetlib.enabling.locators.ServiceRunningInstance; + +public class DiskSpaceComparator implements Comparator { + + @Override + public int compare(final ServiceRunningInstance s0, ServiceRunningInstance s1) { + return Integer.compare(s0.getUsedDiskSpace(), s1.getUsedDiskSpace()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/comparators/HandledDatastructuresComparator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/comparators/HandledDatastructuresComparator.java new file mode 100644 index 0000000..d782e92 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/comparators/HandledDatastructuresComparator.java @@ -0,0 +1,14 @@ +package eu.dnetlib.enabling.locators.comparators; + +import java.util.Comparator; + +import eu.dnetlib.enabling.locators.ServiceRunningInstance; + +public class HandledDatastructuresComparator implements Comparator { + + @Override + public int compare(final ServiceRunningInstance s1, final ServiceRunningInstance s2) { + return Integer.compare(s1.getHandledDatastructures(), s2.getHandledDatastructures()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/comparators/PreferLocalRunningInstanceComparator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/comparators/PreferLocalRunningInstanceComparator.java new file mode 100644 index 0000000..8cd8b63 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/locators/comparators/PreferLocalRunningInstanceComparator.java @@ -0,0 +1,20 @@ +package eu.dnetlib.enabling.locators.comparators; + +import java.util.Comparator; + +import eu.dnetlib.enabling.locators.ServiceRunningInstance; + +public class PreferLocalRunningInstanceComparator implements Comparator { + + @Override + public int compare(final ServiceRunningInstance s1, final ServiceRunningInstance s2) { + if (s1.isLocal()) { + return -1; + } else if (s2.isLocal()) { + return 1; + } else { + return 0; + } + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/resultset/rmi/ResultSetException.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/resultset/rmi/ResultSetException.java new file mode 100644 index 0000000..455c8dc --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/resultset/rmi/ResultSetException.java @@ -0,0 +1,21 @@ +package eu.dnetlib.enabling.resultset.rmi; + +import eu.dnetlib.common.rmi.RMIException; + +public class ResultSetException extends RMIException { + + /** + * + */ + private static final long serialVersionUID = -7130554407601059627L; + + public ResultSetException(Throwable e) { + super(e); + // TODO Auto-generated constructor stub + } + + public ResultSetException(String string) { + super(string); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/resultset/rmi/ResultSetService.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/resultset/rmi/ResultSetService.java new file mode 100644 index 0000000..269dd83 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/resultset/rmi/ResultSetService.java @@ -0,0 +1,142 @@ +package eu.dnetlib.enabling.resultset.rmi; + +import java.util.List; + +import javax.jws.WebMethod; +import javax.jws.WebParam; +import javax.jws.WebService; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.common.rmi.BaseService; + +/** + * ResultSet service interface. + * + * TODO: implement other compatibility methods as needed. + * + * @author marko + * + */ +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public interface ResultSetService extends BaseService { + /** + * create a new pull rs. + * + * @param bdId + * bulk data identifier + * @param initialPageSize + * page size for the polling on the server side. + * @param expiryTime + * RS expiry time + * @return + */ + W3CEndpointReference createPullRSEPR( + @WebParam(name = "dataProviderServiceAddress") W3CEndpointReference dataProviderEPR, + @WebParam(name = "bdId") String bdId, + @WebParam(name = "initialPageSize") int initialPageSize, + @WebParam(name = "expiryTime") int expiryTime, + @WebParam(name = "styleSheet") String styleSheet, + @WebParam(name = "keepAliveTime") Integer keepAliveTime, + @WebParam(name = "total") Integer total); + + /** + * create a new pull rs. + * + * compatibility version + * + * @param bdId + * bulk data identifier + * @param initialPageSize + * page size for the polling on the server side. + * @param expiryTime + * RS expiry time + * @return + */ + W3CEndpointReference createPullRS( + @WebParam(name = "dataProviderServiceAddress") String dataProviderServiceAddress, + @WebParam(name = "bdId") String bdId, + @WebParam(name = "initialPageSize") int initialPageSize, + @WebParam(name = "expiryTime") int expiryTime, + @WebParam(name = "styleSheet") String styleSheet, + @WebParam(name = "keepAliveTime") Integer keepAliveTime, + @WebParam(name = "total") Integer total); + + /** + * close a result set. A closed resultset is guaranteed not to grow. + * + * @param rsId + */ + void closeRS(@WebParam(name = "rsId") String rsId); + + /** + * get one 'page' of results. + * + * TODO: define how results are returned when the range is not present in the result set. + * + * @param fromPosition + * counting from 1 + * @param toPosition + * included + * @param requestMode + * @return a page of data + */ + List getResult( + @WebParam(name = "rsId") String rsId, + @WebParam(name = "fromPosition") int fromPosition, + @WebParam(name = "toPosition") int toPosition, + @WebParam(name = "requestMode") String requestMode) throws ResultSetException; + + /** + * get the number of result elements present in the resultset. + * + * @param rsId + * result set identifier + * @return number of results available in the resultset + * @throws ResultSetException + */ + int getNumberOfElements(@WebParam(name = "rsId") String rsId) throws ResultSetException; + + /** + * create a new push resultset. + * + * @param expiryTime RS expiry time + * @param keepAliveTime keep alive time + * @return epr of new resultset + * @throws ResultSetException + */ + W3CEndpointReference createPushRS(@WebParam(name = "expiryTime") int expiryTime, @WebParam(name = "keepAliveTime") int keepAliveTime) + throws ResultSetException; + + /** + * add new data to a push resultset. + * + * @param rsId resultset id + * @param elements list of elements to be addded + * @return dummy value + * @throws ResultSetException + */ + String populateRS(@WebParam(name = "rsId") String rsId, @WebParam(name = "elements") List elements) throws ResultSetException; + + /** + * return current status of a resultset. + * + * @param rsId resultset id + * @return status + * @throws ResultSetException + */ + String getRSStatus(@WebParam(name = "rsId") String rsId) throws ResultSetException; + + /** + * read a resultset property. + * + * @param rsId resultset id + * @param name property value + * @return property value + * @throws ResultSetException + */ + String getProperty(@WebParam(name = "rsId") String rsId, @WebParam(name = "name") String name) throws ResultSetException; + + @WebMethod(operationName = "identify") + public String identify(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/AbstractEndpointReferenceBuilder.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/AbstractEndpointReferenceBuilder.java new file mode 100644 index 0000000..9d5343a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/AbstractEndpointReferenceBuilder.java @@ -0,0 +1,46 @@ +package eu.dnetlib.soap; + +import java.util.Map; + +import javax.xml.namespace.QName; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * + * default implementation short methods. + * + * @author marko + * + * @param + */ +public abstract class AbstractEndpointReferenceBuilder implements EndpointReferenceBuilder { + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.soap.EndpointReferenceBuilder#getEndpointReference(java.lang.Object) + */ + @Override + public W3CEndpointReference getEndpointReference(final T endpoint) { + return getEndpointReference(endpoint, (Map)null); + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.soap.EndpointReferenceBuilder#getEndpointReference(java.lang.Object, java.lang.String) + */ + @Override + public W3CEndpointReference getEndpointReference(final T endpoint, final String referenceParam) { + return getEndpointReference(endpoint, referenceParam, null); + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.soap.EndpointReferenceBuilder#getEndpointReference(java.lang.Object, java.util.Map) + */ + @Override + public W3CEndpointReference getEndpointReference(final T endpoint, final Map attrs) { + return getEndpointReference(endpoint, null, attrs); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/DataStructureLookupEndpointReferenceBuilder.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/DataStructureLookupEndpointReferenceBuilder.java new file mode 100644 index 0000000..d88918d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/DataStructureLookupEndpointReferenceBuilder.java @@ -0,0 +1,88 @@ +package eu.dnetlib.enabling.soap; + +import java.util.Map; + +import javax.xml.namespace.QName; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.locators.UniqueServiceLocator; +import eu.dnetlib.enabling.tools.OpaqueResource; +import eu.dnetlib.enabling.tools.StringOpaqueResource; +import eu.dnetlib.soap.AbstractEndpointReferenceBuilder; +import eu.dnetlib.soap.EndpointReferenceBuilder; + +/** + * Builds an epr given an ID to a datastructure. + * + * @author marko + * + */ +public class DataStructureLookupEndpointReferenceBuilder extends AbstractEndpointReferenceBuilder implements EndpointReferenceBuilder { + + /** + * service locator. + */ + private UniqueServiceLocator serviceLocator; + + /** + * underlying ds epr builder. + */ + private DataStructureProfileEndpointReferenceBuilder dsEprBuilder; + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.soap.EndpointReferenceBuilder#getAddress(java.lang.Object) + */ + @Override + public String getAddress(final String pid) { + return dsEprBuilder.getAddress(getProfile(pid)); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.soap.EndpointReferenceBuilder#getEndpointReference(java.lang.Object, java.lang.String, java.util.Map) + */ + @Override + public W3CEndpointReference getEndpointReference(final String pid, final String referenceParam, final Map attrs) { + return dsEprBuilder.getEndpointReference(getProfile(pid), attrs); + } + + /** + * obtain the ds profile + * + * @param pid + * datastructure profile + * @return resource + */ + private OpaqueResource getProfile(final String pid) { + try { + return new StringOpaqueResource(serviceLocator.getService(ISLookUpService.class).getResourceProfile(pid)); + } catch (Exception e) { // TODO: remove this hack (conversion to unchecked exception) + throw new IllegalStateException(e); + } + } + + public DataStructureProfileEndpointReferenceBuilder getDsEprBuilder() { + return dsEprBuilder; + } + + @Required + public void setDsEprBuilder(final DataStructureProfileEndpointReferenceBuilder dsEprBuilder) { + this.dsEprBuilder = dsEprBuilder; + } + + public UniqueServiceLocator getServiceLocator() { + return serviceLocator; + } + + @Required + public void setServiceLocator(final UniqueServiceLocator serviceLocator) { + this.serviceLocator = serviceLocator; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/DataStructureProfileEndpointReferenceBuilder.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/DataStructureProfileEndpointReferenceBuilder.java new file mode 100644 index 0000000..885acc1 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/DataStructureProfileEndpointReferenceBuilder.java @@ -0,0 +1,76 @@ +package eu.dnetlib.enabling.soap; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.namespace.QName; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import eu.dnetlib.enabling.tools.OpaqueResource; +import eu.dnetlib.soap.AbstractEndpointReferenceBuilder; +import eu.dnetlib.soap.EndpointReferenceBuilder; +import eu.dnetlib.soap.cxf.CxfEndpointReferenceBuilder; + +/** + * Build an epr from a data structure. + * + * @author marko + * + */ +public class DataStructureProfileEndpointReferenceBuilder extends AbstractEndpointReferenceBuilder implements + EndpointReferenceBuilder { + + private static final Log log = LogFactory.getLog(DataStructureProfileEndpointReferenceBuilder.class); // NOPMD by marko on 11/24/08 5:02 PM + + + /** + * low level epr builder used to create the actual epr. + * + * TODO: factor out the address based epr building out of CXF specific code. + * + */ + private transient CxfEndpointReferenceBuilder lowEprBuilder = new CxfEndpointReferenceBuilder(); + + /** + * {@inheritDoc} + * @see eu.dnetlib.soap.EndpointReferenceBuilder#getAddress(java.lang.Object) + */ + @Override + public String getAddress(OpaqueResource profile) { + return profile.getResourceUri().replace("?wsdl", ""); + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.soap.AbstractEndpointReferenceBuilder#getEndpointReference(java.lang.Object, java.util.Map) + */ + @Override + public W3CEndpointReference getEndpointReference(OpaqueResource profile, Map attrs) { + return getEndpointReference(profile, profile.getResourceId(), attrs); + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.soap.AbstractEndpointReferenceBuilder#getEndpointReference(java.lang.Object) + */ + @Override + public W3CEndpointReference getEndpointReference(OpaqueResource profile) { + log.info("GETTING EPR short"); + + return getEndpointReference(profile, profile.getResourceId()); + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.soap.EndpointReferenceBuilder#getEndpointReference(java.lang.Object, java.lang.String, java.util.Map) + */ + @Override + public W3CEndpointReference getEndpointReference(OpaqueResource profile, String referenceParam, Map attrs) { + return lowEprBuilder.getEndpointReference(getAddress(profile), null, null, getAddress(profile) + "?wsdl", referenceParam, + new HashMap()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/EndpointReferenceBuilder.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/EndpointReferenceBuilder.java new file mode 100644 index 0000000..c15dc52 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/EndpointReferenceBuilder.java @@ -0,0 +1,96 @@ +package eu.dnetlib.soap; + +import java.util.Map; + +import javax.xml.namespace.QName; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * This is a generalization of org.apache.cxf.jaxws.EndpointReferenceBuilder and org.apache.cxf.jaxws.EndpointImpl + * + *

+ * javax.xml.ws.WebServiceContext org.apache.cxf.jaxws.EndpointReferenceBuilder doesn't expose the API for adding + * additional metadata while org.apache.cxf.jaxws.EndpointImpl doesn't take the correct endpoint address from the + * EndpointInfo object. org.apache.cxf.endpoint.Endpoint return a CXF proprietary endpoint reference and not the + * javax/w3c standard definition. + *

+ * + *

+ * This interface is intended to provide an abstract way to construct an endpoint reference for a given service, + * depending on the type of endpoint interface you have at hand (CXF abstract endpoint or jaxws endpoint) + *

+ * + *

+ * Normally the type parameter T will be bound to your endpoint type. + *

+ * + *

+ * In CXF jaxws applications you can easly get a WebServiceContext instance which returns an EndpointReference, however + * the API is cumbersome because it requires instantiating w3c DOM Element instances for each reference parameter, and + * it doesn't allow setting custom metadata elements. + *

+ * + *

+ * Implementors of this API will extract as many useful informations from the runtime besides the plain soap endpoint + * address. + *

+ * + * @author marko + * @param + * all endpoint builders are parameterized to specific endpoint type which on the used framework (not on the + * service) + * + */ +public interface EndpointReferenceBuilder { + /** + * get an endpoint reference with default metadata attached. + * + * @param endpoint + * the endpoint + * @return an endpoint reference + */ + W3CEndpointReference getEndpointReference(T endpoint); + + /** + * get an endpoint reference with custom metadata attached. + * + * @param endpoint + * the endpoint + * @param attrs + * metadata attribute map + * @return an endpoint reference + */ + W3CEndpointReference getEndpointReference(T endpoint, Map attrs); + + /** + * get an endpoint reference with a WSA reference parameter. + * + * @param endpoint + * the endpoint + * @param referenceParam + * reference parameters + * @return an endpoint reference + */ + W3CEndpointReference getEndpointReference(T endpoint, String referenceParam); + + /** + * get an endpoint reference with custom metadata attached and WSA reference parameter. + * + * @param endpoint + * endpoint + * @param referenceParam + * reference parameters + * @param attrs + * metadata attribute map + * @return an endpoint reference + */ + W3CEndpointReference getEndpointReference(T endpoint, String referenceParam, Map attrs); + + /** + * Sometimes we need only the address. + * + * @param endpoint endpoint + * @return address + */ + String getAddress(T endpoint); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/StaticEndpointReferenceBuilder.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/StaticEndpointReferenceBuilder.java new file mode 100644 index 0000000..35d6313 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/StaticEndpointReferenceBuilder.java @@ -0,0 +1,60 @@ +package eu.dnetlib.soap; + +import java.util.Map; + +import javax.xml.namespace.QName; +import javax.xml.ws.wsaddressing.W3CEndpointReference; +import javax.xml.ws.wsaddressing.W3CEndpointReferenceBuilder; + +/** + * This endpoint reference builder builds always the same epr, with a fixed address, for any incoming endpoint (even + * null endpoints). Useful when registering service profiles for external services like the old perl Aggregator. + * + * @author marko + * + * @param + * endpoint type + */ +public class StaticEndpointReferenceBuilder implements EndpointReferenceBuilder { + + /** + * service address. + */ + private String address; + + @Override + public String getAddress(final T endpoint) { + return address; + } + + @Override + public W3CEndpointReference getEndpointReference(final T endpoint) { + final W3CEndpointReferenceBuilder builder = new W3CEndpointReferenceBuilder(); + builder.address(address); + return builder.build(); + } + + @Override + public W3CEndpointReference getEndpointReference(final T endpoint, final Map attrs) { + return getEndpointReference(endpoint); + } + + @Override + public W3CEndpointReference getEndpointReference(final T endpoint, final String referenceParam) { + return getEndpointReference(endpoint); + } + + @Override + public W3CEndpointReference getEndpointReference(final T endpoint, final String referenceParam, final Map attrs) { + return getEndpointReference(endpoint); + } + + public String getAddress() { + return address; + } + + public void setAddress(final String address) { + this.address = address; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/cxf/CxfEndpointReferenceBuilder.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/cxf/CxfEndpointReferenceBuilder.java new file mode 100644 index 0000000..3c23a54 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/cxf/CxfEndpointReferenceBuilder.java @@ -0,0 +1,206 @@ +package eu.dnetlib.soap.cxf; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.ws.wsaddressing.W3CEndpointReference; +import javax.xml.ws.wsaddressing.W3CEndpointReferenceBuilder; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.cxf.endpoint.Endpoint; +import org.apache.oro.text.perl.Perl5Util; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import eu.dnetlib.soap.AbstractEndpointReferenceBuilder; + +/** + * The cxf endpoint is an internal cross-toolkit implementation of the messaging endpoint. + * + *

+ * End users will normally access jaxws endpoints, since jaxws is the mostly used cxf frontend. However, generic code, + * like interceptors, will handle cxf endpoints, so sometimes you may need to construct endpoint references from them. + *

+ * + *
+ * <ns3:Address>http://localhost:8090/app/services/isStore</ns3:Address>
+ * <ns3:ReferenceParameters/>
+ * <ns3:Metadata>
+ *   <wsaw:InterfaceName>ns1:ISStoreService</wsaw:InterfaceName>
+ *   <wsaw:ServiceName EndpointName="ISStoreServicePort">ns2:ISStoreServiceService</wsaw:ServiceName>
+ *   <infrastructure:infrastructure>development</infrastructure:infrastructure>
+ * </ns3:Metadata>
+ * 
+ * + * Users can easily define default system or service wide custom metadata to endpoint references by setting a + * defaultMetadata property: + * + *
+ * <bean name="cxfEndpointReferenceBuilder" class="eu.dnetlib.soap.cxf.CxfEndpointReferenceBuilder">
+ *  <property name="defaultMetadata">
+ *   <map>
+ *    <entry key="{http://dnetlib.eu/endpointReference}infrastructure" value="${infrastructure.name}" />
+ *   </map>
+ *  </property>
+ * </bean>
+ * 
+ * + * + * @author marko + * + */ +public class CxfEndpointReferenceBuilder extends AbstractEndpointReferenceBuilder { + /** + * logger. + */ + private static final Log log = LogFactory.getLog(CxfEndpointReferenceBuilder.class); // NOPMD by marko on 11/24/08 4:55 + + /** + * users can put some default metadata elements into all EPRs. + */ + private Map defaultMetadata; + + /** + * regexp utility. + */ + private final transient Perl5Util matcher = new Perl5Util(); + + /** + * namespace of the ResourceIdentifier reference parameter. + */ + private String riNamespace = "http://www.driver.org"; + + /** + * element name of the ResourceIdentifier reference parameter. + */ + private String riElementName = "ResourceIdentifier"; + + /** + * {@inheritDoc} + * + * TODO: refactor. + * + * @see eu.dnetlib.soap.EndpointReferenceBuilder#getEndpointReference(java.lang.Object, java.util.Map) + */ + + @Override + public W3CEndpointReference getEndpointReference(final Endpoint endpoint, final String referenceParam, final Map attributes) { + final String address = getAddress(endpoint); + return getEndpointReference(address, endpoint.getService().getName(), endpoint.getEndpointInfo().getName(), null, referenceParam, + attributes); + } + + /** + * low level method which allows the construction of a endpoint reference knowing all basic data as the address, service name etc. + * + * @param address + * @param serviceName + * @param endpointName + * @param wsdl + * @param referenceParam + * @param attributes + * @return + */ + public W3CEndpointReference getEndpointReference( + final String address, + final QName serviceName, + final QName endpointName, + final String wsdl, + final String referenceParam, + final Map attributes) { + Map attrs = attributes; + + final W3CEndpointReferenceBuilder builder = new W3CEndpointReferenceBuilder(); + builder.address(address); + if(serviceName != null) + builder.serviceName(serviceName); + if(endpointName != null) + builder.endpointName(endpointName); + builder.wsdlDocumentLocation(wsdl); + + if (defaultMetadata != null) { + if (attrs == null) + attrs = new HashMap(); + for (Entry entry : defaultMetadata.entrySet()) + attrs.put(splitQNameString(entry.getKey()), entry.getValue()); + } + + try { + final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); // NOPMD + if (referenceParam != null) { + final Element referenceElement = doc.createElementNS(getRiNamespace(), getRiElementName()); + referenceElement.setTextContent(referenceParam); + builder.referenceParameter(referenceElement); + } + + if (attrs != null && !attrs.isEmpty()) { + for (Entry entry : attrs.entrySet()) { + final QName qname = entry.getKey(); + final Element element = doc.createElementNS(qname.getNamespaceURI(), qname.getLocalPart()); + element.setTextContent((String) entry.getValue()); + builder.metadata(element); + } + } + } catch (ParserConfigurationException e) { + log.fatal("cannot extend EPR", e); + throw new IllegalStateException("cannot extend EPR", e); + } + + return builder.build(); + } + + /** + * compute an endpoint address. + * + * @param endpoint + * endpoint + * @return url as string + */ + @Override + public String getAddress(final Endpoint endpoint) { + return endpoint.getEndpointInfo().getAddress(); + } + + /** + * helper method for converting "{namespace}elementName" strings to QNames. + * + * @param key + * "{namespace}elementName" string + * @return qname + */ + private QName splitQNameString(final String key) { + matcher.match("m/{(.*)}(.*)/", key); + + return new QName(matcher.group(1), matcher.group(2)); + } + + public Map getDefaultMetadata() { + return defaultMetadata; + } + + public void setDefaultMetadata(final Map defaultMetadata) { + this.defaultMetadata = defaultMetadata; + } + + public String getRiNamespace() { + return riNamespace; + } + + public void setRiNamespace(final String riNamespace) { + this.riNamespace = riNamespace; + } + + public String getRiElementName() { + return riElementName; + } + + public void setRiElementName(final String riElementName) { + this.riElementName = riElementName; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/cxf/JaxwsEndpointReferenceBuilder.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/cxf/JaxwsEndpointReferenceBuilder.java new file mode 100644 index 0000000..5e1d7df --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/cxf/JaxwsEndpointReferenceBuilder.java @@ -0,0 +1,64 @@ +package eu.dnetlib.soap.cxf; + +import java.util.Map; + +import javax.xml.namespace.QName; +import javax.xml.ws.Endpoint; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.apache.cxf.jaxws.EndpointImpl; +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.soap.AbstractEndpointReferenceBuilder; + +/** + * This EndpointReferenceBuilder implementation takes a jaxws endpoint and extracts the cxf endpoint from it. + * + * jaxws endpoints are the most readily available endpoint objects since they constructed from jaxws:endpoint spring + * beans, as shown in the CXF documentation. + * + * Since this implementation forwards the job to CxfEndpointReferenceBuilder, the 'builder' property has to be set: + * + *
+ * <bean name="jaxwsEndpointReferenceBuilder" class="eu.dnetlib.soap.cxf.JaxwsEndpointReferenceBuilder"
+ *   p:builder-ref="cxfEndpointReferenceBuilder" />
+ * 
+ * + * @author marko + * @see CxfEndpointReferenceBuilder + * + */ +public class JaxwsEndpointReferenceBuilder extends AbstractEndpointReferenceBuilder { + + /** + * required reference to the cxf endpoint builder. + */ + private CxfEndpointReferenceBuilder builder = null; + + /** + * simply unpacks the cxf endpoint from the jaxws endpoint and forwards it to CxfEndpointReferenceBuilder. + * + * {@inheritDoc} + * + * @see eu.dnetlib.soap.EndpointReferenceBuilder#getEndpointReference(java.lang.Object, java.util.Map) + */ + @Override + public W3CEndpointReference getEndpointReference(final Endpoint endpoint, final String referenceParam, final Map attrs) { + return builder.getEndpointReference(((EndpointImpl) endpoint).getServer().getEndpoint(), referenceParam, attrs); + } + + public CxfEndpointReferenceBuilder getBuilder() { + return builder; + } + + @Required + public void setBuilder(final CxfEndpointReferenceBuilder builder) { + this.builder = builder; + } + + @Override + public String getAddress(Endpoint endpoint) { + return builder.getAddress(((EndpointImpl) endpoint).getServer().getEndpoint()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/cxf/StandaloneCxfEndpointReferenceBuilder.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/cxf/StandaloneCxfEndpointReferenceBuilder.java new file mode 100644 index 0000000..938a58e --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/soap/cxf/StandaloneCxfEndpointReferenceBuilder.java @@ -0,0 +1,97 @@ +package eu.dnetlib.soap.cxf; + +import java.net.URI; +import java.net.URISyntaxException; + +import javax.annotation.PostConstruct; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.cxf.endpoint.Endpoint; +import org.springframework.beans.factory.annotation.Required; + +/** + * CxfEndpointReferenceBuilder is not able to create correct endpoint addresses outside a http request context. This means that service + * initialization code cannot obtain the service address and thus cannot register himself. + * + * This subclass allows putting a local address (ip/dns + port) to be used when the runtime servlet context is not available. + * + * TODO: automated tomcat port detection, trough org.apache.catalina.ServerFactory.getServer().getServices() TODO: automated jetty port + * detection + * + * + * @author marko + * + */ +public class StandaloneCxfEndpointReferenceBuilder extends CxfEndpointReferenceBuilder { + + private static final Log log = LogFactory.getLog(StandaloneCxfEndpointReferenceBuilder.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * base url where all services are exported. + */ + private String baseAddress; + + private String absoluteBase; + + private boolean forceLocalAddress = false; + + public String getBaseAddress() { + return baseAddress; + } + + public void setBaseAddress(final String baseAddress) { + this.baseAddress = baseAddress; + } + + @PostConstruct + public void init() throws URISyntaxException { + URI base = new URI(baseAddress); + log.info("base address: " + baseAddress); + + this.absoluteBase = (new URI(base.getScheme(), base.getUserInfo(), base.getHost(), base.getPort(), null, null, null)).toString().trim(); + log.info("absolute base address: " + absoluteBase); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.soap.cxf.CxfEndpointReferenceBuilder#getAddress(org.apache.cxf.endpoint.Endpoint) + */ + @Override + public String getAddress(final Endpoint endpoint) { + final String address = super.getAddress(endpoint); + + if (forceLocalAddress) { + try { + URI uri = new URI(address); + if (!address.startsWith("http://")) { + String res = baseAddress + uri.getPath(); + if (log.isDebugEnabled()) { + log.debug("fixing address to: " + res); + } + return res; + } + String res = absoluteBase + uri.getPath(); + if (log.isDebugEnabled()) { + log.debug("forcing address to: " + res); + } + return res; + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + if (!address.startsWith("http://") && (baseAddress != null)) { return baseAddress + address; } + return address; + } + + public boolean isForceLocalAddress() { + return forceLocalAddress; + } + + @Required + public void setForceLocalAddress(final boolean forceLocalAddress) { + this.forceLocalAddress = forceLocalAddress; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/AbstractBaseService.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/AbstractBaseService.java new file mode 100644 index 0000000..0957d65 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/AbstractBaseService.java @@ -0,0 +1,85 @@ +package eu.dnetlib.enabling.tools; + +import javax.jws.WebService; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.Lifecycle; + +import eu.dnetlib.common.rmi.BaseService; + +/** + * This class contains default definition for BaseService contract and basic service lifecycle. + * + * TODO: split BaseService contract implementation from lifecycle and other helper method + * + * @author marko + * + */ +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public abstract class AbstractBaseService implements BaseService, Lifecycle { + + /** + * logger. + */ + private static final Log log = LogFactory // NOPMD by marko on 11/24/08 5:02 PM + .getLog(AbstractBaseService.class); + + private boolean started = false; + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.common.rmi.BaseService#identify() + */ + @Override + public String identify() { + return getClass().getName(); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.common.rmi.BaseService#notify(java.lang.String, java.lang.String, java.lang.String, java.lang.String) + */ + @Override + public void notify(final String subscriptionId, final String topic, final String isId, final String message) { + log.debug("got notification: " + topic + ", profile: " + isId + ", body: " + message); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.common.rmi.BaseService#start() + */ + @Override + public void start() { + log.info("Starting service " + identify()); + if (started) { + log.warn("Service " + this + "already started, check bean initializations!"); + } + started = true; + } + + /** + * {@inheritDoc} + * + * @see org.springframework.context.Lifecycle#isRunning() + */ + @Override + public boolean isRunning() { + log.debug("called isRunning " + this); + return false; + } + + /** + * {@inheritDoc} + * + * @see org.springframework.context.Lifecycle#stop() + */ + @Override + public void stop() { + log.info("Stopping service " + this); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/AbstractServiceLocator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/AbstractServiceLocator.java new file mode 100644 index 0000000..7e9002a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/AbstractServiceLocator.java @@ -0,0 +1,119 @@ +package eu.dnetlib.enabling.tools; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Map; + +import javax.annotation.Resource; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.ws.wsaddressing.W3CEndpointReference; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import com.google.common.collect.Maps; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.soap.cxf.StandaloneCxfEndpointReferenceBuilder; + +@Deprecated +public abstract class AbstractServiceLocator implements ServiceLocator { + + /** + * lookup locator. + */ + @Resource(name="lookupLocator") + private ServiceLocator lookUpLocator; + + /** + * build epr. + */ + @Resource + private StandaloneCxfEndpointReferenceBuilder eprBuilder; + + /** + * service resolver. used to create proxies for discovered services. + */ + @Resource(name="serviceResolver") + private ServiceResolver serviceResolver; + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(AbstractServiceLocator.class); // NOPMD by marko on 11/24/08 5:02 PM + + + @Override + public T getService(final String profileId, final Class clazz) { + final String profile = executeQuery(profileId, null); + + try { + final XPathFactory factory = XPathFactory.newInstance(); + final XPath xpath = factory.newXPath(); + + final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + final DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + + final Document doc = docBuilder.parse(new InputSource(new StringReader(profile))); + final String url = xpath.evaluate("//url", doc); + final String serviceId = xpath.evaluate("//id", doc); + + final NodeList propElements = (NodeList) xpath.evaluate("//PROPERTY", doc, XPathConstants.NODESET); + final Map props = Maps.newHashMap(); + + for (int i = 0; i < propElements.getLength(); i++) { + Element propElement = (Element) propElements.item(i); + props.put(propElement.getAttribute("key"), propElement.getAttribute("value")); + } + + final W3CEndpointReference epr = eprBuilder.getEndpointReference(url, null, null, url + "?wsdl", null, null); + + final ServiceRunningInstance instance = new ServiceRunningInstance(epr, serviceId, url, props); + + return serviceResolver.getService(clazz, instance.getEpr()); + } catch(Exception e) { + log.error("cannot instantiate service from id: " + profileId, e); + throw new IllegalStateException("cannot instantiate service from id: " + profileId, e); + } + } + + @Override + public String getServiceId(final String profileId) { + return executeQuery(profileId, "/RESOURCE_PROFILE/HEADER/RESOURCE_IDENTIFIER/@value/string()"); + } + + private String executeQuery(final String profileId, final String xpath) { + final StringWriter sw = new StringWriter(); + sw.append("let $uri:=/RESOURCE_PROFILE/HEADER[./RESOURCE_IDENTIFIER/@value='"); + sw.append(profileId); + sw.append("']/RESOURCE_URI/@value/string()"); + sw.append("\n\n"); + sw.append("for $x in collection('/db/DRIVER/ServiceResources')/RESOURCE_PROFILE/HEADER"); + sw.append("\n"); + sw.append("where $x/RESOURCE_PROFILE/HEADER/RESOURCE_URI/@value = $uri"); + sw.append("\n"); + sw.append("return $x"); + if (xpath != null) { + sw.append(xpath); + } + final String xq = sw.toString(); + + try { + return lookUpLocator.getService().getResourceProfileByQuery(xq); + } catch (ISLookUpException e) { + log.error("cannot locate service using query: " + xq, e); + throw new IllegalStateException("cannot locate service using query: " + xq, e); + } + } + + +} \ No newline at end of file diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/AbstractServiceResolverImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/AbstractServiceResolverImpl.java new file mode 100644 index 0000000..63e9fec --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/AbstractServiceResolverImpl.java @@ -0,0 +1,32 @@ +package eu.dnetlib.enabling.tools; + +import javax.xml.transform.dom.DOMResult; +import javax.xml.ws.wsaddressing.W3CEndpointReference; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +/** + * implement common functionality of ServiceResolvers. + * + * @author marko + * + */ +public abstract class AbstractServiceResolverImpl implements ServiceResolver { + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.tools.ServiceResolver#getResourceIdentifier(javax.xml.ws.wsaddressing.W3CEndpointReference) + */ + @Override + public String getResourceIdentifier(final W3CEndpointReference epr) { + final DOMResult dom = new DOMResult(); + epr.writeTo(dom); + + try { + return XPathFactory.newInstance().newXPath().evaluate("//*[local-name() = 'ResourceIdentifier']", dom.getNode()); + } catch (XPathExpressionException e) { + throw new IllegalStateException("cannot construct xpath expression", e); + } + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DOMOpaqueResource.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DOMOpaqueResource.java new file mode 100644 index 0000000..6c298e1 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DOMOpaqueResource.java @@ -0,0 +1,264 @@ +package eu.dnetlib.enabling.tools; + +import java.io.StringWriter; +import java.util.Date; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import eu.dnetlib.miscutils.datetime.DateUtils; + +/** + * OpaqueResource holding a plain old DOM document. + * + * @author marko + * + */ +public class DOMOpaqueResource implements OpaqueResource { + /** + * xpath expression error message. + */ + private static final String XPATH_ERROR = "cannot compile xpath expression"; + + /** + * value attribute. + */ + private static final String VALUE_ATTR = "value"; + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(DOMOpaqueResource.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * resource identifier. + */ + private String resourceId; + + /** + * resource type. + */ + private String resourceType; + + /** + * resource kind. + */ + private String resourceKind; + + /** + * resource uri. + */ + private String resourceUri; + + /** + * modification time stamp. + */ + private Date modificationDate; + + /** + * original document DOM. + */ + private Document dom; + + /** + * xslt transformer instance. + */ + private Transformer transformer; + + /** + * construct a DOMOpaqueInstance from a W3C DOM document. + * + * @param dom + * DOM document + * @throws XPathExpressionException + * happens + */ + public DOMOpaqueResource(final Document dom) throws XPathExpressionException { + this.dom = dom; + + try { + transformer = TransformerFactory.newInstance().newTransformer(); + } catch (TransformerConfigurationException e) { + throw new IllegalStateException("transformer configuration", e); + } catch (TransformerFactoryConfigurationError e) { + throw new IllegalStateException("transformer configuration", e); + } + + final XPath xpath = XPathFactory.newInstance().newXPath(); + + this.resourceId = xpath.evaluate("/RESOURCE_PROFILE/HEADER/RESOURCE_IDENTIFIER/@value", dom); + this.resourceType = xpath.evaluate("/RESOURCE_PROFILE/HEADER/RESOURCE_TYPE/@value", dom); + this.resourceKind = xpath.evaluate("/RESOURCE_PROFILE/HEADER/RESOURCE_KIND/@value", dom); + this.resourceUri = xpath.evaluate("/RESOURCE_PROFILE/HEADER/RESOURCE_URI/@value", dom); + + String modificationDateSource = xpath.evaluate("/RESOURCE_PROFILE/HEADER/DATE_OF_CREATION/@value", dom); + + try { + this.modificationDate = new DateUtils().parse(modificationDateSource); + } catch (IllegalStateException e) { + log.debug("invalid date '" + modificationDateSource + "'", e); + } + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.OpaqueResource#asDom() + */ + @Override + public Document asDom() { + return getDom(); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.OpaqueResource#asString() + */ + @Override + public String asString() { + final StringWriter writer = new StringWriter(); + + try { + transformer.transform(new DOMSource(getDom()), new StreamResult(writer)); + } catch (TransformerException e) { + log.fatal("cannot serialize document", e); + return null; + } + return writer.toString(); + } + + public Document getDom() { + return dom; + } + + public void setDom(final Document dom) { + this.dom = dom; + } + + @Override + public String getResourceId() { + return resourceId; + } + + @Override + public String getResourceType() { + return resourceType; + } + + public void setResourceType(final String resourceType) { + this.resourceType = resourceType; + } + + @Override + public String getResourceKind() { + return resourceKind; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.OpaqueResource#setResourceKind(java.lang.String) + */ + @Override + public void setResourceKind(final String resourceKind) { + try { + final XPath xpath = XPathFactory.newInstance().newXPath(); + final Element kindEl = (Element) xpath.evaluate("/RESOURCE_PROFILE/HEADER/RESOURCE_KIND", asDom(), XPathConstants.NODE); + kindEl.setAttribute(VALUE_ATTR, resourceKind); + this.resourceKind = resourceKind; + } catch (XPathExpressionException e) { + throw new IllegalStateException(XPATH_ERROR, e); + } + + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.OpaqueResource#setResourceId(java.lang.String) + */ + @Override + public void setResourceId(final String identifier) { + try { + final XPath xpath = XPathFactory.newInstance().newXPath(); + final Element idEl = (Element) xpath.evaluate("/RESOURCE_PROFILE/HEADER/RESOURCE_IDENTIFIER", asDom(), XPathConstants.NODE); + idEl.setAttribute(VALUE_ATTR, identifier); + resourceId = identifier; + } catch (XPathExpressionException e) { + throw new IllegalStateException(XPATH_ERROR, e); + } + } + + @Override + public Date getModificationDate() { + return modificationDate; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.OpaqueResource#setModificationDate(java.util.Date) + */ + @Override + public void setModificationDate(final Date modificationDate) { + try { + final XPath xpath = XPathFactory.newInstance().newXPath(); + final Element idEl = (Element) xpath.evaluate("/RESOURCE_PROFILE/HEADER/DATE_OF_CREATION", asDom(), XPathConstants.NODE); + if (idEl == null) { + log.warn("resource with type " + getResourceType() + " has no date of creation element"); + return; + } + idEl.setAttribute(VALUE_ATTR, new DateUtils(modificationDate).getDateAsISO8601String()); + this.modificationDate = modificationDate; + } catch (XPathExpressionException e) { + throw new IllegalStateException(XPATH_ERROR, e); + } + + } + + @Override + public String getResourceUri() { + return resourceUri; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.OpaqueResource#setResourceUri(java.lang.String) + */ + @Override + public void setResourceUri(final String resourceUri) { + try { + final XPath xpath = XPathFactory.newInstance().newXPath(); + final Element uriEl = (Element) xpath.evaluate("/RESOURCE_PROFILE/HEADER/RESOURCE_URI", asDom(), XPathConstants.NODE); + uriEl.setAttribute(VALUE_ATTR, resourceUri); + this.resourceUri = resourceUri; + } catch (XPathExpressionException e) { + throw new IllegalStateException(XPATH_ERROR, e); + } + } + + public Transformer getTransformer() { + return transformer; + } + + public void setTransformer(final Transformer transformer) { + this.transformer = transformer; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DefaultServiceLocatorLocationScorer.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DefaultServiceLocatorLocationScorer.java new file mode 100644 index 0000000..874c7f1 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DefaultServiceLocatorLocationScorer.java @@ -0,0 +1,107 @@ +package eu.dnetlib.enabling.tools; + +import java.net.MalformedURLException; +import java.net.URL; + +import javax.annotation.Resource; + +import eu.dnetlib.soap.cxf.StandaloneCxfEndpointReferenceBuilder; + +/** + * Assign better scores for near services. Can be configured. + * + * @author marko + * + */ +@Deprecated +public class DefaultServiceLocatorLocationScorer implements DynamicServiceLocatorLocationScorer { + + /** + * default score assigned when the other service has the same host. + */ + private static final int LOCAL_HOST_SCORE = 5; + + /** + * default score assigned when the other service has the same host and port (same container). + */ + private static final int LOCAL_PORT_SCORE = 10; + + /** + * default score assigned when the other service has the same host and port (same container and context). + */ + private static final int LOCAL_SRV_SCORE = 15; + + + /** + * score assigned when the other service has the same host. + */ + private int localHostScore = LOCAL_HOST_SCORE; + + /** + * score assigned when the other service has the same host and port (same container). + */ + private int localPortScore = LOCAL_PORT_SCORE; + + /** + * score assigned when the other service has the same host and port (same container and context). + */ + private int localSrvScore = LOCAL_SRV_SCORE; + + /** + * build epr. + */ + @Resource + private StandaloneCxfEndpointReferenceBuilder eprBuilder; + + /** + * {@inheritDoc} + * @throws MalformedURLException + * @see eu.dnetlib.enabling.tools.DynamicServiceLocatorLocationScorer#score(java.net.URL) + */ + @Override + public int score(final URL url) throws MalformedURLException { + final URL localBase = new URL(eprBuilder.getBaseAddress()); + if (url.toString().startsWith(localBase.toString())) + return localSrvScore; + + if (localBase.getHost().equals(url.getHost())) { + if (localBase.getPort() == url.getPort()) + return localPortScore; + return localHostScore; + } + return 0; + } + + public StandaloneCxfEndpointReferenceBuilder getEprBuilder() { + return eprBuilder; + } + + public void setEprBuilder(final StandaloneCxfEndpointReferenceBuilder eprBuilder) { + this.eprBuilder = eprBuilder; + } + + public int getLocalHostScore() { + return localHostScore; + } + + public void setLocalHostScore(final int localHostScore) { + this.localHostScore = localHostScore; + } + + public int getLocalPortScore() { + return localPortScore; + } + + public void setLocalPortScore(final int localPortScore) { + this.localPortScore = localPortScore; + } + + public int getLocalSrvScore() { + return localSrvScore; + } + + public void setLocalSrvScore(final int localSrvScore) { + this.localSrvScore = localSrvScore; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicHNMLocator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicHNMLocator.java new file mode 100644 index 0000000..6ac18f4 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicHNMLocator.java @@ -0,0 +1,62 @@ +package eu.dnetlib.enabling.tools; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpDocumentNotFoundException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.locators.UniqueServiceLocator; + +/** + * Locates a registered HNM running on a given url. + * + * @author marko + * + */ +@Deprecated +public class DynamicHNMLocator implements HNMLocator { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(DynamicHNMLocator.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * service locator. + */ + UniqueServiceLocator serviceLocator; + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.HNMLocator#getHNMForUrl(java.lang.String) + */ + @Override + public String getHNMForUrl(final String url) { + + final String prefix = url.substring(0, url.indexOf('/', "http://".length())); + final String query = "collection('')//RESOURCE_PROFILE[.//RESOURCE_TYPE/@value = 'HostingNodeManagerServiceResourceType' and starts-with(.//RESOURCE_URI/@value, '" + + prefix + "')]//RESOURCE_IDENTIFIER/@value/string()"; + + try { + return serviceLocator.getService(ISLookUpService.class).getResourceProfileByQuery(query); + } catch (final ISLookUpDocumentNotFoundException e) { + log.debug("doument not found for query: " + query); + return null; + } catch (final ISLookUpException e) { + throw new IllegalStateException("cannot search hnm", e); + } + } + + public UniqueServiceLocator getServiceLocator() { + return serviceLocator; + } + + @Required + public void setServiceLocator(UniqueServiceLocator serviceLocator) { + this.serviceLocator = serviceLocator; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicServiceEnumerator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicServiceEnumerator.java new file mode 100644 index 0000000..9ab9b70 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicServiceEnumerator.java @@ -0,0 +1,182 @@ +package eu.dnetlib.enabling.tools; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Resource; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.ws.wsaddressing.W3CEndpointReference; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Required; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.tools.registration.ServiceNameResolver; +import eu.dnetlib.miscutils.collections.EnsureCollection; +import eu.dnetlib.soap.cxf.StandaloneCxfEndpointReferenceBuilder; + +/** + * Enumerates all services of a given type. + * + * @author marko + * + * @param + * service class + */ +@Deprecated +public class DynamicServiceEnumerator implements ServiceEnumerator { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(DynamicServiceEnumerator.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * service class + */ + private Class clazz; + + /** + * lookup locator. + */ + private ServiceLocator lookUpLocator; + + /** + * service name resolver. + */ + @Resource + private ServiceNameResolver serviceNameResolver; // NOPMD + + /** + * build epr. + */ + @Resource + private StandaloneCxfEndpointReferenceBuilder eprBuilder; + + /** + * default constructor, useful for spring based instantiation. + */ + public DynamicServiceEnumerator() { + // default + } + + /** + * Build a dynamic service enumerator for a given class + * + * @param clazz + * class + */ + public DynamicServiceEnumerator(final Class clazz) { + super(); + this.clazz = clazz; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.ServiceEnumerator#getServices() + */ + @Override + public List> getServices() { + final String serviceName = serviceNameResolver.getName(clazz); + log.debug("searching for service: " + serviceName); + + final String xquery = "for $x in collection('/db/DRIVER/ServiceResources')//RESOURCE_PROFILE[.//RESOURCE_TYPE/@value/string() = '" + + serviceName + + "ResourceType'] return {$x//RESOURCE_IDENTIFIER/@value/string()}{$x//PROTOCOL[@name = 'SOAP']/@address/string()}{$x//SERVICE_PROPERTIES/PROPERTY}"; + log.debug(xquery); + + final XPathFactory factory = XPathFactory.newInstance(); + final XPath xpath = factory.newXPath(); + + final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + + try { + final DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + + final List services = lookUpLocator.getService().quickSearchProfile(xquery); + final List> instances = new ArrayList>(); + + for (final String source : EnsureCollection.list(services)) { + final Document doc = docBuilder.parse(new InputSource(new StringReader(source))); + final String url = xpath.evaluate("//url", doc); + final String serviceId = xpath.evaluate("//id", doc); + + final NodeList propElements = (NodeList) xpath.evaluate("//PROPERTY", doc, XPathConstants.NODESET); + Map props = new HashMap(); + + for (int i = 0; i < propElements.getLength(); i++) { + Element propElement = (Element) propElements.item(i); + props.put(propElement.getAttribute("key"), propElement.getAttribute("value")); + } + + final W3CEndpointReference epr = eprBuilder.getEndpointReference(url, null, null, url + "?wsdl", null, null); + + instances.add(new ServiceRunningInstance(epr, serviceId, url, props)); + } + + return instances; + } catch (final ISLookUpException e) { + throw new IllegalStateException("cannot locate service " + serviceName, e); + } catch (final XPathExpressionException e) { + throw new IllegalStateException("cannot locate service " + serviceName, e); + } catch (final SAXException e) { + throw new IllegalStateException("cannot locate service " + serviceName, e); + } catch (final IOException e) { + throw new IllegalStateException("cannot locate service " + serviceName, e); + } catch (final ParserConfigurationException e) { + throw new IllegalStateException("cannot locate service " + serviceName, e); + } + } + + public Class getClazz() { + return clazz; + } + + @Required + public void setClazz(final Class clazz) { + this.clazz = clazz; + } + + public ServiceLocator getLookUpLocator() { + return lookUpLocator; + } + + public void setLookUpLocator(final ServiceLocator lookUpLocator) { + this.lookUpLocator = lookUpLocator; + } + + public ServiceNameResolver getServiceNameResolver() { + return serviceNameResolver; + } + + public void setServiceNameResolver(final ServiceNameResolver serviceNameResolver) { + this.serviceNameResolver = serviceNameResolver; + } + + public StandaloneCxfEndpointReferenceBuilder getEprBuilder() { + return eprBuilder; + } + + public void setEprBuilder(StandaloneCxfEndpointReferenceBuilder eprBuilder) { + this.eprBuilder = eprBuilder; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicServiceLocator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicServiceLocator.java new file mode 100644 index 0000000..120ca5a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicServiceLocator.java @@ -0,0 +1,205 @@ +package eu.dnetlib.enabling.tools; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.tools.registration.ServiceNameResolver; +import eu.dnetlib.soap.cxf.StandaloneCxfEndpointReferenceBuilder; + +/** + * Locates a service through dynamic service discovery. + * + * @author marko + * + * @param + */ +@Deprecated +public class DynamicServiceLocator extends AbstractServiceLocator { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(DynamicServiceLocator.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * service interface. + */ + private Class clazz; + + /** + * lookup locator. + */ + private ServiceLocator lookUpLocator; + + /** + * service name resolver. + */ + @Resource + private ServiceNameResolver serviceNameResolver; // NOPMD + + /** + * build epr. + * TODO: obsolete, replace by full enumerator injection + */ + @Resource + private StandaloneCxfEndpointReferenceBuilder eprBuilder; + + /** + * service resolver. used to create proxies for discovered services. + */ + private ServiceResolver serviceResolver; + + /** + * delegate the score computation to this component. By default use a DefaultServiceLocatorLocationScorer. + */ + private DynamicServiceLocatorLocationScorer scorer; + + /** + * service enumerator. + */ + private ServiceEnumerator enumerator; + + /** + * By default use a DefaultServiceLocatorLocationScorer if no scorer is defined. + */ + @PostConstruct + protected void init() { + if (scorer == null) { + final DefaultServiceLocatorLocationScorer tmp = new DefaultServiceLocatorLocationScorer(); + tmp.setEprBuilder(eprBuilder); + scorer = tmp; + } + + if (enumerator == null) { + final DynamicServiceEnumerator tmp = new DynamicServiceEnumerator(clazz); + tmp.setLookUpLocator(lookUpLocator); + tmp.setServiceNameResolver(serviceNameResolver); + tmp.setEprBuilder(eprBuilder); + enumerator = tmp; + } + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.ServiceLocator#getService() + */ + @Override + public T getService() { + return serviceResolver.getService(clazz, findFirstCandidate().getEpr()); + } + + @Override + public String getServiceId() { + return findFirstCandidate().getServiceId(); + } + + private ServiceRunningInstance findFirstCandidate() { + final String serviceName = serviceNameResolver.getName(clazz); + log.debug("searching for service: " + serviceName); + + // TODO: backward compat hack + if(enumerator == null) { + log.warn("Enumerator is null in " + this + ". Postconstruct not called by spring, please check. Now called manually to workaround this problem"); + init(); + } + + + final List> candidates = enumerator.getServices(); + if (candidates == null || candidates.isEmpty()) + throw new IllegalStateException("cannot locate service " + serviceName + ", no matching service profile found"); + + Collections.sort(candidates, new Comparator>() { + @Override + public int compare(final ServiceRunningInstance o1, final ServiceRunningInstance o2) { + try { + final Integer u1Score = computeScore(new URL(o1.getUrl())); + final Integer u2Score = computeScore(new URL(o2.getUrl())); + return -u1Score.compareTo(u2Score); + } catch (MalformedURLException e) { + log.warn("ignoring service with malformed url", e); + return 0; + } + } + }); + log.debug(candidates); + + return candidates.get(0); + } + + /** + * compute the score for a given service url. + * + * @param url + * url to be scored + * @return score + * @throws MalformedURLException + * happens + */ + protected int computeScore(final URL url) throws MalformedURLException { + return scorer.score(url); + } + + public Class getClazz() { + return clazz; + } + + @Required + public void setClazz(final Class clazz) { + this.clazz = clazz; + } + + public ServiceLocator getLookUpLocator() { + return lookUpLocator; + } + + @Required + public void setLookUpLocator(final ServiceLocator lookUpLocator) { + this.lookUpLocator = lookUpLocator; + } + + public ServiceNameResolver getServiceNameResolver() { + return serviceNameResolver; + } + + public void setServiceNameResolver(final ServiceNameResolver serviceNameResolver) { // NOPMD + this.serviceNameResolver = serviceNameResolver; + } + + public StandaloneCxfEndpointReferenceBuilder getEprBuilder() { + return eprBuilder; + } + + public void setEprBuilder(final StandaloneCxfEndpointReferenceBuilder eprBuilder) { + this.eprBuilder = eprBuilder; + } + + @Required + public ServiceResolver getServiceResolver() { + return serviceResolver; + } + + public void setServiceResolver(final ServiceResolver serviceResolver) { + this.serviceResolver = serviceResolver; + } + + public DynamicServiceLocatorLocationScorer getScorer() { + return scorer; + } + + public void setScorer(final DynamicServiceLocatorLocationScorer scorer) { + this.scorer = scorer; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicServiceLocatorLocationScorer.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicServiceLocatorLocationScorer.java new file mode 100644 index 0000000..59f3684 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/DynamicServiceLocatorLocationScorer.java @@ -0,0 +1,21 @@ +package eu.dnetlib.enabling.tools; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Implementors of this interface provide custom methods to assign scores to services based solely on their location. + * @author marko + * + */ +@Deprecated +public interface DynamicServiceLocatorLocationScorer { + /** + * Compute the score assigned to a given service location (url). + * + * @param url service url + * @return score (the higher the better) + * @throws MalformedURLException could happen + */ + int score(URL url) throws MalformedURLException; +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/HNMLocator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/HNMLocator.java new file mode 100644 index 0000000..a308efe --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/HNMLocator.java @@ -0,0 +1,19 @@ +package eu.dnetlib.enabling.tools; + +/** + * finds an HNM profile for a giver service url. + * + * @author marko + * + */ +public interface HNMLocator { + + /** + * finds an HNM profile for a giver service url. + * + * @param url + * service address + * @return hnm id + */ + String getHNMForUrl(String url); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/JaxwsServiceResolverImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/JaxwsServiceResolverImpl.java new file mode 100644 index 0000000..77bfce9 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/JaxwsServiceResolverImpl.java @@ -0,0 +1,25 @@ +package eu.dnetlib.enabling.tools; + +import javax.xml.ws.WebServiceFeature; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * This service resolver asks jaxws to obtain a proxy to a remote service. + * + * @author marko + * + */ +public class JaxwsServiceResolverImpl extends AbstractServiceResolverImpl implements ServiceResolver { + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.tools.ServiceResolver#getService(java.lang.Class, javax.xml.ws.wsaddressing.W3CEndpointReference) + */ + @Override + public T getService(final Class clazz, final W3CEndpointReference epr) { + synchronized(this) { + return epr.getPort(clazz, new WebServiceFeature[] {}); + } + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/LocalServiceResolverImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/LocalServiceResolverImpl.java new file mode 100644 index 0000000..34960cb --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/LocalServiceResolverImpl.java @@ -0,0 +1,91 @@ +package eu.dnetlib.enabling.tools; + +import java.io.StringReader; +import java.util.Map; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.xml.sax.InputSource; + +/** + * This service resolver parses the EPR and returns a local reference to a service instance if the EPR refers to the + * local node. + * + * @author marko + * + */ +public class LocalServiceResolverImpl extends AbstractServiceResolverImpl implements ServiceResolver, ApplicationContextAware { + private static final Log log = LogFactory.getLog(LocalServiceResolverImpl.class); // NOPMD by marko on 11/24/08 5:02 PM + + private String baseAddress; + + private ApplicationContext applicationContext; + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.ServiceResolver#getService(java.lang.Class, + * javax.xml.ws.wsaddressing.W3CEndpointReference) + */ + @Override + public T getService(final Class clazz, final W3CEndpointReference epr) { + + // backward compat + if (baseAddress == null) { + log.warn("please set baseAddress in " + this); + return null; + } + + try { + // TODO: measure performance impact of this. I wrote it this way just to be sure of thread safety + String address = XPathFactory.newInstance().newXPath().evaluate("/*[local-name() ='EndpointReference']/*[local-name() = 'Address']", + new InputSource(new StringReader(epr.toString()))); + if (log.isDebugEnabled()) + log.debug("epr address " + address); + + if (address.startsWith(baseAddress)) + return resolveLocalService(clazz, epr); + } catch (XPathExpressionException e) { + log.warn("cannot parse epr", e); + } + + return null; + } + + private T resolveLocalService(final Class clazz, W3CEndpointReference epr) { + log.debug("resolving local service " + clazz); + + Map services = (Map) applicationContext.getBeansOfType(clazz, false, false); + + log.debug("found services: " + services); + if(services.size() > 0) + for(T service : services.values()) + return service; + + return null; + } + + public String getBaseAddress() { + return baseAddress; + } + + public void setBaseAddress(String baseAddress) { + this.baseAddress = baseAddress; + } + + public ApplicationContext getApplicationContext() { + return applicationContext; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/NullHNMLocator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/NullHNMLocator.java new file mode 100644 index 0000000..30dc2d0 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/NullHNMLocator.java @@ -0,0 +1,21 @@ +package eu.dnetlib.enabling.tools; + +/** + * simplest HNM locator: doesn't find any. + * + * @author marko + * + */ +public class NullHNMLocator implements HNMLocator { + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.HNMLocator#getHNMForUrl(java.lang.String) + */ + @Override + public String getHNMForUrl(final String url) { + return ""; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/OpaqueResource.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/OpaqueResource.java new file mode 100644 index 0000000..7cab9f7 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/OpaqueResource.java @@ -0,0 +1,98 @@ +package eu.dnetlib.enabling.tools; + +import java.util.Date; + +import org.w3c.dom.Document; + +/** + * Some services, in particular the enabling layer, needs to manipulate all types of resources without knowing the exact + * structure of their content, but only some well defined properties defined here. + * + *

+ * Different wrappers can be provided for different kind of resources. For example resources returned from the xmldb + * layer can be cheaply mapped to an OpaqueResource, without going to full xml->bean conversion. + *

+ * + * @author marko + * + */ +public interface OpaqueResource { + /** + * Resource type. + * + * @return resource type string + */ + String getResourceType(); + + /** + * Resource kind. + * + * @return resource kind string + */ + String getResourceKind(); + + /** + * Resource identifier. + * + * @return resource identifier string + */ + String getResourceId(); + + /** + * Resource URI. + * + * @return resource uri string + */ + String getResourceUri(); + + /** + * get modification time stamp. + * + * @return time stamp + */ + Date getModificationDate(); + + /** + * Implementors may need to serialize the resource to a xml string representation. + * + * @return xml serialization + */ + String asString(); + + /** + * Implementors may store the DOM in the first place. Otherwise they should + * return a parsed w3c DOM instance. + * + * @return DOM document + */ + Document asDom(); + + /** + * change the resource identifier. + * + * @param identifier new identifier + */ + void setResourceId(String identifier); + + /** + * change the resource kind. + * + * @param kind new kind + */ + void setResourceKind(String kind); + + /** + * change the resource uri. + * + * @param uri new uri + */ + void setResourceUri(String uri); + + /** + * set modification timestamp. + * + * @param timeStamp modification time stamp + */ + void setModificationDate(Date timeStamp); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ResourceIdentifierResolver.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ResourceIdentifierResolver.java new file mode 100644 index 0000000..1c6add9 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ResourceIdentifierResolver.java @@ -0,0 +1,30 @@ +package eu.dnetlib.enabling.tools; + +/** + * Resource identifiers may carry, implicitly or explicity, some informations about the location of the resource in the + * hierarchical xmldb namespace. + * + * Implementors of this interface may take different approachs to the problem. + * + * @author marko + * + */ +public interface ResourceIdentifierResolver { + /** + * get the xmldb filename associated to this resource identifier, usually extracted from the identifier itself. + * + * @param resId resource identifier + * @return xmldb file name + */ + String getFileName(String resId); + + /** + * get the xmldb collection name associated to this resource identifier. + * Implementors may decide to encode it in the identifier itself or use a secondary associative memory. + * to quickly transform identifiers. + * + * @param resId resource identifier + * @return xmldb collection name + */ + String getCollectionName(String resId); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ResourceLoaderHelper.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ResourceLoaderHelper.java new file mode 100644 index 0000000..1c48148 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ResourceLoaderHelper.java @@ -0,0 +1,44 @@ +package eu.dnetlib.enabling.tools; + +import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternUtils; + +/** + * Some beans aren't directly instantiated (like quartz jobs for instance), so they cannot receive the resource loader + * by implementing the ResourceLoaderAware interface. + * + * This class is a temporary solution to this problem until we find a way to obtain a bean reference to the + * application context (the actual resource loader) itself and inject it directly. It could be done by turning this + * class into a bean factory and expose the resource loader through it. + * + * @author marko + * + */ +public class ResourceLoaderHelper implements ResourceLoaderAware { + + /** + * injected resource loader. + */ + private ResourceLoader resourceLoader; + + /** + * obtain a resource pattern resolver for a given resource loader. + * + * @return pattern resolver + */ + public ResourcePatternResolver getResourcePatternResolver() { + return ResourcePatternUtils.getResourcePatternResolver(getResourceLoader()); + } + + public ResourceLoader getResourceLoader() { + return resourceLoader; + } + + @Override + public void setResourceLoader(final ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceEnumerator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceEnumerator.java new file mode 100644 index 0000000..c791616 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceEnumerator.java @@ -0,0 +1,19 @@ +package eu.dnetlib.enabling.tools; + +import java.util.List; + +/** + * A service enumerator returns a bunch of service descriptions. The logic depends on the service enumerator. + * + * @author marko + * + */ +@Deprecated +public interface ServiceEnumerator { + /** + * Obtain a list of services. + * + * @return a list of service running instance descriptions + */ + List> getServices(); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceLocator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceLocator.java new file mode 100644 index 0000000..2c4abaa --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceLocator.java @@ -0,0 +1,47 @@ +package eu.dnetlib.enabling.tools; + +/** + * A service locator provides a reference to a, possibly remote, service. + * + * @author marko + * + * @param + * the type of the service to return + * @deprecated As of release 2.0.0, use instead {@link eu.dnetlib.enabling.locators.DefaultUniqueServiceLocator} + */ +@Deprecated +public interface ServiceLocator { + + /** + * locate and return a service of this type. + * + * @return a service client instance + */ + T getService(); + + /** + * Locate using a profileID (service or datastructure) the service of this type. + * + * @param profileId + * @param clazz + * @return a service client instance + */ + T getService(final String profileId, final Class clazz); + + /** + * locate and return a service ID. + * + * @return a service ID + */ + String getServiceId(); + + /** + * locate and return a service ID. + * + * @param profileId + * (the id of the service or the id of one of its datastructures) + * @return a service ID. + */ + String getServiceId(final String profileId); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceLocatorChain.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceLocatorChain.java new file mode 100644 index 0000000..d1225e3 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceLocatorChain.java @@ -0,0 +1,69 @@ +package eu.dnetlib.enabling.tools; + +import java.util.Collection; + +/** + * This class allows chaining several service locators and return the first one matching. Usually this is usefully when + * we want a StaticLocator as a fallback if the DynamicLocator cannot find anything. + * + * @author marko + * + * @param + * service type + */ +@Deprecated +public class ServiceLocatorChain extends AbstractServiceLocator { + + /** + * service locators, iterated in order. + */ + private Collection> locators; + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.ServiceLocator#getService() + */ + @Override + public T getService() { + IllegalStateException lastException = null; // NOPMD + + for (ServiceLocator locator : locators) { + try { + return locator.getService(); + } catch (IllegalStateException e) { + lastException = e; + } + } + if (lastException != null) { + throw new IllegalStateException("cannot find any matching service. Last locator in the chain reported as cause", lastException); + } + throw new IllegalStateException("cannot find any matching service: the service locator chain is empty"); + } + + @Override + public String getServiceId() { + IllegalStateException lastException = null; // NOPMD + + for (ServiceLocator locator : locators) { + try { + return locator.getServiceId(); + } catch (IllegalStateException e) { + lastException = e; + } + } + if (lastException != null) { + throw new IllegalStateException("cannot find any matching service. Last locator in the chain reported as cause", lastException); + } + throw new IllegalStateException("cannot find any matching service: the service locator chain is empty"); + } + + public Collection> getLocators() { + return locators; + } + + public void setLocators(final Collection> locators) { + this.locators = locators; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceResolver.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceResolver.java new file mode 100644 index 0000000..3888ecb --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceResolver.java @@ -0,0 +1,32 @@ +package eu.dnetlib.enabling.tools; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * Instances of this type resolve EPR references to service endpoints. + * + * @author marko + * + */ +public interface ServiceResolver { + /** + * return a service client given an EPR instance. + * + * @param + * the service type + * @param clazz + * needed for type inference of type parameter + * @param epr + * EPR + * @return a service client reference + */ + T getService(Class clazz, W3CEndpointReference epr); + + /** + * Extract the resource identifier embedded in the EPR. + * + * @param epr end point reference + * @return resource identifier + */ + String getResourceIdentifier(W3CEndpointReference epr); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceResolverChain.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceResolverChain.java new file mode 100644 index 0000000..a512c25 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceResolverChain.java @@ -0,0 +1,61 @@ +package eu.dnetlib.enabling.tools; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * This resolver chains a list of resolvers. + * + * @author marko + * + */ +public class ServiceResolverChain implements ServiceResolver { + + /** + * service resolvers in decreasing order or priority. + */ + private List resolvers = new ArrayList(); + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.ServiceResolver#getService(java.lang.Class, + * javax.xml.ws.wsaddressing.W3CEndpointReference) + */ + @Override + public T getService(final Class clazz, final W3CEndpointReference epr) { + for (ServiceResolver resolver : getResolvers()) { + final T service = resolver.getService(clazz, epr); + + if (service != null) + return service; + } + return null; + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.tools.ServiceResolver#getResourceIdentifier(javax.xml.ws.wsaddressing.W3CEndpointReference) + */ + @Override + public String getResourceIdentifier(final W3CEndpointReference epr) { + for (ServiceResolver resolver : getResolvers()) { + final String rsId = resolver.getResourceIdentifier(epr); + + if (rsId != null) + return rsId; + } + return null; + } + + public List getResolvers() { + return resolvers; + } + + public void setResolvers(final List resolvers) { + this.resolvers = resolvers; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceRunningInstance.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceRunningInstance.java new file mode 100644 index 0000000..cc1bfba --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/ServiceRunningInstance.java @@ -0,0 +1,95 @@ +package eu.dnetlib.enabling.tools; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * This bean packages the minimum information for describing a service running instance. + * + * @author marko + * + */ +@Deprecated +public class ServiceRunningInstance { + /** + * service endpoint. + */ + private W3CEndpointReference epr; + + /** + * service resource id. + */ + private String serviceId; + + /** + * service endpoint url. + */ + private String url; + + /** + * arbitrary service properties stored in the profile. + */ + private Map serviceProperties; + + /** + * Create a service running instance. + * + * @param epr service epr + * @param serviceId service profile resource id + * @param url service endpoint url + */ + public ServiceRunningInstance(final W3CEndpointReference epr, final String serviceId, final String url) { + this(epr, serviceId, url, new HashMap()); + } + + /** + * Create a service running instance. + * + * @param epr service epr + * @param serviceId service profile resource id + * @param url service endpoint url + * @param serviceProperties service property map + */ + public ServiceRunningInstance(final W3CEndpointReference epr, final String serviceId, final String url, final Map serviceProperties) { + super(); + this.epr = epr; + this.serviceId = serviceId; + this.url = url; + this.serviceProperties = serviceProperties; + } + + public W3CEndpointReference getEpr() { + return epr; + } + + public void setEpr(final W3CEndpointReference epr) { + this.epr = epr; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(final String serviceId) { + this.serviceId = serviceId; + } + + public Map getServiceProperties() { + return serviceProperties; + } + + public void setServiceProperties(final Map serviceProperties) { + this.serviceProperties = serviceProperties; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/SplittedQueryExecutor.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/SplittedQueryExecutor.java new file mode 100644 index 0000000..fbf1b42 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/SplittedQueryExecutor.java @@ -0,0 +1,171 @@ +package eu.dnetlib.enabling.tools; + +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Required; + +import com.google.common.collect.Lists; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpDocumentNotFoundException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.locators.UniqueServiceLocator; +import eu.dnetlib.miscutils.collections.PositionalStringMapGenerator; + +/** + * Utility class which interacts with a lookup, performs a query and fills a java domain object splitting the result into constructor + * arguments. + * + * @author marko + * + */ +public class SplittedQueryExecutor { + + /** + * service locator. + */ + private UniqueServiceLocator serviceLocator; + + /** + * default constructor. lookupLocator has to be injected. + */ + public SplittedQueryExecutor() { + // empty + } + + /** + * Commodity constructor for constructor injection. + * + * @param lookupLocator + * lookup locator + */ + public SplittedQueryExecutor(final UniqueServiceLocator serviceLocator) { + super(); + this.serviceLocator = serviceLocator; + } + + /** + * Performs the query, splits the result at ":-:". + * + * @param + * domain class type + * @param clazz + * domain class + * @param query + * xquery + * @return iterable of domain class instances + */ + public Iterable query(final Class clazz, final String query) { + return query(clazz, query, ":-:"); + } + + /** + * Performs the query, splits the result at separator + * + * @param + * domain class type + * @param clazz + * domain class + * @param query + * xquery + * @param separator + * split separator + * @return iterable of domain class instances + */ + public Iterable query(final Class clazz, final String query, final String separator) { + return new PositionalStringMapGenerator().split(clazz, performQuery(query), separator); + } + + /** + * Return a list of maps of splitted query results + * + * @param query + * xquery + * @param keys + * list of keys + * @return collection of key/value pairs + */ + public Iterable> query(final String query, final String... keys) { + return new PositionalStringMapGenerator(keys).split(performQuery(query), ":-:"); + } + + /** + * Like query(String, String..) but returns a container that whose values can be modified. + * + * @param query + * xquery + * @param keys + * list of keys + * @return mutable collection of key/value pairs + */ + public Iterable> mutableQuery(final String query, final String... keys) { + return Lists.newArrayList(query(query, keys)); + } + + /** + * Like query(Class, String), but returns a container which can be modified. + * + * @param + * some domain class + * @param clazz + * domain class + * @param query + * xquery + * @return mutable collection of X + */ + public Iterable mutableQuery(final Class clazz, final String query) { + return Lists.newArrayList(query(clazz, query)); + } + + /** + * Like query(Class, String, String), but returns a container which can be modified. + * + * @param + * some domain class + * @param clazz + * domain class + * @param query + * xquery + * @param separator + * separator + * @return mutable collection of X + */ + public Iterable mutableQuery(final Class clazz, final String query, final String separator) { + return Lists.newArrayList(query(clazz, query, separator)); + } + + public List performQuery(final String query) { + try { + return serviceLocator.getService(ISLookUpService.class).quickSearchProfile(query); + } catch (final ISLookUpException e) { + throw new IllegalStateException(e); + } + } + + /** + * Fetch only one string result. + * + * @param query + * @return null if no such result + */ + public String queryFirst(final String query) { + try { + return serviceLocator.getService(ISLookUpService.class).getResourceProfileByQuery(query); + } catch (ISLookUpDocumentNotFoundException e) { + return null; + } catch (final ISLookUpException e) { + throw new IllegalStateException(e); + } + + } + + public UniqueServiceLocator getServiceLocator() { + return serviceLocator; + } + + @Required + public void setServiceLocator(final UniqueServiceLocator serviceLocator) { + this.serviceLocator = serviceLocator; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/StaticServiceLocator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/StaticServiceLocator.java new file mode 100644 index 0000000..1787647 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/StaticServiceLocator.java @@ -0,0 +1,91 @@ +package eu.dnetlib.enabling.tools; + +import java.util.Map; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Required; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +import eu.dnetlib.enabling.tools.registration.ServiceRegistrationManager; + +/** + * This service locator returns always one service instance. + * + * Usually declared as a spring bean an injected in service when a static configuration is needed. + * + * @author marko + * + * @param + * the type of the service + */ +@Deprecated +public class StaticServiceLocator extends AbstractServiceLocator implements ApplicationContextAware { + + /** + * static service reference. + */ + private T service; + + private ApplicationContext applicationContext; + + /** + * default constructor. initialize with setter. + */ + public StaticServiceLocator() { + // no operation + } + + /** + * construct a static service locator pointing at a given service. + * + * @param service + * service + */ + public StaticServiceLocator(final T service) { + this.service = service; + } + + /** + * The usual usage pattern of service locators is to call chain a direct call + * on the result of this method. In order to avoid nullpointer exception, this method throws an exception + * with a better explanation, in case the service is null. + * + * TODO: consider using checked exceptions. + * + * {@inheritDoc} + * @see eu.dnetlib.enabling.tools.ServiceLocator#getService() + */ + @Override + public T getService() { + if (service == null) { + throw new IllegalStateException("cannot find service, check configuration"); + } + return service; + } + + @Override + public String getServiceId() { + final Map beans = applicationContext.getBeansOfType(ServiceRegistrationManager.class); + + if (beans != null) { + for (ServiceRegistrationManager r : beans.values()) { + if (r.getService() == getService()) { + return r.getServiceProfile().getResourceId(); + } + } + } + throw new IllegalStateException("cannot find service, check configuration"); + } + + @Required + public void setService(final T service) { + this.service = service; + } + + @Override + public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/StreamOpaqueResource.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/StreamOpaqueResource.java new file mode 100644 index 0000000..9a089a6 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/StreamOpaqueResource.java @@ -0,0 +1,54 @@ +package eu.dnetlib.enabling.tools; // NOPMD + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPathExpressionException; + +import org.apache.commons.io.IOUtils; +import org.xml.sax.SAXException; + +/** + * implements a possibly optimized OpaqueResource constructed from a stream containing the xml source. + * + *

the optimization consists of postponing the parsing as possibly and parse the profile passing the stream + * directly to the xml parser.

+ * + *

+ * TODO: implement the optimization, currenly converts to string and reuses StringOpaqueResource + *

+ * + * @author marko + * + */ +public class StreamOpaqueResource extends StringOpaqueResource { + + /** + * construct with an input stream. + * + * @param source source stream of xml text + * @throws XPathExpressionException shouldn't happen + * @throws SAXException could happen + * @throws IOException could happen + * @throws ParserConfigurationException shouldn't happen + */ + public StreamOpaqueResource(final InputStream source) throws XPathExpressionException, SAXException, IOException, ParserConfigurationException { + super(stringify(source)); + } + + /** + * helper method: reads a stream to a string. + * + * @param source input stream + * @return string containing the whole stream content. + * @throws IOException could happen. + */ + private static String stringify(final InputStream source) throws IOException { + final StringWriter writer = new StringWriter(); + IOUtils.copy(source, writer); + return writer.getBuffer().toString(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/StringOpaqueResource.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/StringOpaqueResource.java new file mode 100644 index 0000000..77d0e6a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/StringOpaqueResource.java @@ -0,0 +1,48 @@ +package eu.dnetlib.enabling.tools; // NOPMD + +import java.io.IOException; +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPathExpressionException; + +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Opaque resource initialized from a string. + * + * Currently it extends a DOMOpaqueResource, but in future it should postpone xml parsing as late as possible. + * + * @author marko + * + */ +public class StringOpaqueResource extends DOMOpaqueResource { + + /** + * construct an opaque resource from a xml source string. + * + * @param source xml source + * @throws XPathExpressionException happens + * @throws SAXException happens + * @throws IOException happens + * @throws ParserConfigurationException happens + */ + public StringOpaqueResource(final String source) throws XPathExpressionException, SAXException, IOException, ParserConfigurationException { + super(getBuilderFactory().newDocumentBuilder().parse(new InputSource(new StringReader(source)))); + + } + + /** + * get a namespace aware document builder factory. + * + * @return document builder factory. + */ + private static DocumentBuilderFactory getBuilderFactory() { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + return factory; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/UniqueIdentifierGenerator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/UniqueIdentifierGenerator.java new file mode 100644 index 0000000..8259536 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/UniqueIdentifierGenerator.java @@ -0,0 +1,25 @@ +package eu.dnetlib.enabling.tools; + +/** + * genrate unique identifiers. + * + * @author marko + * + */ +public interface UniqueIdentifierGenerator { + + /** + * generate a new unique identifier. + * + * @return string identifier + */ + String generateIdentifier(); + + /** + * helps identify valid identifiers. + * + * @return string regex + */ + String getRegEx(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/UniqueIdentifierGeneratorImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/UniqueIdentifierGeneratorImpl.java new file mode 100644 index 0000000..785fda3 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/UniqueIdentifierGeneratorImpl.java @@ -0,0 +1,74 @@ +package eu.dnetlib.enabling.tools; + +import java.util.UUID; + +/** + * generates unique UUID identifiers. + * + * @author marko + * + */ +public class UniqueIdentifierGeneratorImpl implements UniqueIdentifierGenerator { + + /** + * helps identify valid identifiers. + */ + public static final String UUID_REGEX = "[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"; + + /** + * prefixes all identifiers with this string. + */ + private String prefix = ""; + + /** + * create a new instance with an empty prefix. + */ + public UniqueIdentifierGeneratorImpl() { + // + } + + /** + * construct a new instance with a given prefix. + * + * @param prefix prefix + */ + public UniqueIdentifierGeneratorImpl(final String prefix) { + this.prefix = prefix; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(final String prefix) { + this.prefix = prefix; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.UniqueIdentifierGenerator#generateIdentifier() + */ + @Override + public String generateIdentifier() { + return prefix + createUUID(); + } + + /** + * the real job of creating the uuid is here. + * + * @return new uuid + */ + private String createUUID() { + return UUID.randomUUID().toString(); + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.tools.UniqueIdentifierGenerator#getRegEx() + */ + @Override + public String getRegEx() { + return UUID_REGEX; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/XQueryUtils.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/XQueryUtils.java new file mode 100644 index 0000000..7522251 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/XQueryUtils.java @@ -0,0 +1,39 @@ +package eu.dnetlib.enabling.tools; + +/** + * Implementations of this interface will provide functionalities useful to create xqueries or parts of thereof, like + * xmldb collection names for a given profile. + * + * @author marko + * + */ +public interface XQueryUtils { + /** + * obtain the name of the xmldb collection for a given resource. + * @param resource resource + * @return path of the collection relative to the xmldb dnet root. + */ + String getCollectionPath(OpaqueResource resource); + + /** + * obtain the absolute xmldb collection path for a given resource. + * @param resource resource + * @return collection absolute path + */ + String getCollectionAbsPath(OpaqueResource resource); + + /** + * get the absolute xmldb collection path. + * + * @return root xmldb root collection path + */ + String getRootCollection(); + + /** + * Return the xmldb filename for a given resource. + * + * @param resource resource + * @return xmldb filename + */ + String getFileName(OpaqueResource resource); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/BlackboardServiceRegistrator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/BlackboardServiceRegistrator.java new file mode 100644 index 0000000..cca9cbb --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/BlackboardServiceRegistrator.java @@ -0,0 +1,40 @@ +package eu.dnetlib.enabling.tools.registration; + +import javax.xml.ws.Endpoint; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.is.sn.rmi.ISSNService; +import eu.dnetlib.enabling.tools.OpaqueResource; +import eu.dnetlib.enabling.tools.StringOpaqueResource; + +/** + * This service registrator also subscribes the service to it's own blackboard. + * + * XXX: this class is a hack. It only works with the ValidatingServiceRegistrationManager + * + * @author marko + * + */ +public class BlackboardServiceRegistrator extends ServiceRegistrator { + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.registration.ServiceRegistrator#validateProfile(java.lang.String) + */ + @Override + public String validateProfile(final String profId, final Endpoint endpoint) { + final String profileId = super.validateProfile(profId, endpoint); + + try { + final OpaqueResource serviceProfile = new StringOpaqueResource(getServiceLocator().getService(ISLookUpService.class).getResourceProfile(profileId)); + + getServiceLocator().getService(ISSNService.class, true).subscribe(getEprBuilder().getEndpointReference(endpoint), + "UPDATE/" + serviceProfile.getResourceType() + "/" + profileId + "/RESOURCE_PROFILE/BODY/BLACKBOARD/LAST_REQUEST", 0); + } catch (Exception e) { + throw new IllegalStateException("cannot validate service profile", e); + } + return profileId; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/InterfaceServiceNameResolver.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/InterfaceServiceNameResolver.java new file mode 100644 index 0000000..e7964fe --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/InterfaceServiceNameResolver.java @@ -0,0 +1,74 @@ +package eu.dnetlib.enabling.tools.registration; + +import java.lang.annotation.Annotation; + +import javax.jws.WebService; + +/** + * This service name resolver tries to find the real service name through various heuristics based on the existence of the WebService + * annotation on some interface the service implements. + * + *

+ * NOTE: The search for the interface is depth first on interfaces and then on subclasses. + *

+ * + * @author marko + * + */ +public class InterfaceServiceNameResolver implements ServiceNameResolver { + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.registration.ServiceNameResolver#getName(java.lang.Object) + */ + @Override + public String getName(final Object service) { + + Class res = findInterface(WebService.class, service.getClass()); + if (res == null) { + res = service.getClass(); + } + + return getName(res); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.registration.ServiceNameResolver#getName(java.lang.Class) + */ + @Override + public String getName(final Class iface) { + return iface.getSimpleName(); + } + + /** + * recursively searches a given annotation and returns the class/interface which contains it. + * + * @param + * annotation type + * @param annotation + * annotation to search + * @param clazz + * root of the class hierarchy. + * @return class which contains annotation 'annotation' + */ + private Class findInterface(final Class annotation, final Class clazz) { + if (clazz == null) return null; + + final T ann = clazz.getAnnotation(annotation); + if ((ann != null) && clazz.isInterface()) return clazz; + + for (Class iface : clazz.getInterfaces()) { + final Class ires = findInterface(annotation, iface); + if (ires != null) return ires; + } + + final Class sres = findInterface(annotation, clazz.getSuperclass()); + if (sres != null) return sres; + + return null; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/InterfaceServiceNameResolverCompatibility.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/InterfaceServiceNameResolverCompatibility.java new file mode 100644 index 0000000..514129a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/InterfaceServiceNameResolverCompatibility.java @@ -0,0 +1,45 @@ +package eu.dnetlib.enabling.tools.registration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Allow manual (external) override of service types, through an appropriate mapping (e.g. injected with spring). + * + * @author marko + * + */ +public class InterfaceServiceNameResolverCompatibility extends InterfaceServiceNameResolver { + + /** + * Service names resolved with InterfaceServiceNameResolver will be used as keys in this map. If there is a match, + * the mapping is used instead of the inferred name. + */ + private Map mapping = new HashMap(); + + public Map getMapping() { + return mapping; + } + + public void setMapping(final Map mapping) { + this.mapping = mapping; + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.tools.registration.InterfaceServiceNameResolver#getName(java.lang.Object) + */ + @Override + public String getName(final Class iface) { + final String name = super.getName(iface); + + final String override = getMapping().get(name); + if (override != null) + return override; + + if(name.charAt(0) == 'I' && Character.isUpperCase(name.charAt(1)) && !Character.isUpperCase(name.charAt(2))) + return name.substring(1); + + return name; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceNameResolver.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceNameResolver.java new file mode 100644 index 0000000..41e9832 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceNameResolver.java @@ -0,0 +1,26 @@ +package eu.dnetlib.enabling.tools.registration; + +/** + * Infers the actual name of the service from a service instance. + * + * @author marko + * + */ +public interface ServiceNameResolver { + /** + * returns the service name for a given service. + * + * @param service service instance + * @return service name + */ + String getName(Object service); + + + /** + * Get the service name for a given service interface. + * + * @param serviceInterface service interface + * @return service name. + */ + String getName(Class serviceInterface); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceRegistrationManager.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceRegistrationManager.java new file mode 100644 index 0000000..9d560cc --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceRegistrationManager.java @@ -0,0 +1,55 @@ +package eu.dnetlib.enabling.tools.registration; + +import javax.xml.ws.Endpoint; + +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.tools.OpaqueResource; + +/** + * A service registration manager manages the registration of the service profile for a given service. + * + * @author marko + * + */ +public interface ServiceRegistrationManager { + /** + * service to register. + * + * @param service service instance + */ + @Required + void setService(Object service); + + /** + * service endpoint. + * + * @param endpoint service endpoint + */ + @Required + void setEndpoint(Endpoint endpoint); + + /** + * set to true if we want to disable service registration. + * + * @param disabled + */ + @Required + void setDisabled(boolean disabled); + + /** + * returns the service profile associated with the service. + * + * @return service profile + */ + OpaqueResource getServiceProfile(); + + /** + * returns the registered service + * + * @return the service + */ + Object getService(); + + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceRegistrationManagerImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceRegistrationManagerImpl.java new file mode 100644 index 0000000..052c527 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceRegistrationManagerImpl.java @@ -0,0 +1,366 @@ +package eu.dnetlib.enabling.tools.registration; + +import static eu.dnetlib.enabling.tools.registration.ServiceRegistrationManagerImpl.State.PENDING; +import static eu.dnetlib.enabling.tools.registration.ServiceRegistrationManagerImpl.State.REGISTERED; +import static eu.dnetlib.enabling.tools.registration.ServiceRegistrationManagerImpl.State.UNKNOWN; +import static eu.dnetlib.enabling.tools.registration.ServiceRegistrationManagerImpl.State.UNREGISTERED; + +import java.io.IOException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.ws.Endpoint; +import javax.xml.xpath.XPathExpressionException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Required; +import org.xml.sax.SAXException; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpDocumentNotFoundException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.locators.UniqueServiceLocator; +import eu.dnetlib.enabling.tools.OpaqueResource; +import eu.dnetlib.enabling.tools.StringOpaqueResource; + +/** + * Simple service registration manager implementation. + * + *

+ * The simplest way to use of the service registration manager is to declare the following in your bean definition file: + *

+ * + *
+ * 	<template:instance name="serviceRegistrationManager"
+ *  t:name="myServiceRegistrationManager" t:service="myService" t:endpoint="myServiceEndpoint"
+ *  t:jobScheduler="jobScheduler"/>
+ * 
+ * + *

+ * The ServiceRegistrationManager requires periodic ticks from a quartz job scheduler (the 'jobScheduler' bean in the above example) + *

+ * + *

+ * See the ValidatingServiceRegistrationManagerImpl class for a special implementation which performs automatic service profile validation. + *

+ * + * @see ValidatingServiceRegistrationManagerImpl + * @author marko + * + */ +public class ServiceRegistrationManagerImpl implements ServiceRegistrationManager { + + /** + * error message: the profile is not found. This actually happens a lot, because of the way the old ISLookUpService API was concieved. + */ + private static final String PROFILE_NOT_FOUND = "profile not found"; + + /** + * error message: some error during searches for service profile state. + */ + private static final String ERROR_CHECK = "error checking profile"; + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(ServiceRegistrationManagerImpl.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * registration manager states. + * + * @author marko + * + */ + public enum State { + /** + * service profile is either registered or registered and we have to check out if the profile already exists. + */ + UNKNOWN, + /** + * service profile is not yet registered or the registration disappeared. + */ + UNREGISTERED, /** + * the service profile is registered for validation. A validation operation is awaited. + */ + PENDING, /** + * the service profile is fully registered. + */ + REGISTERED + } + + /** + * service profile identifier. + */ + private String profileId; + + /** + * service to be registered. + */ + private Object service; + + /** + * service endpoint. + */ + private Endpoint endpoint; + + /** + * service locator. + */ + private UniqueServiceLocator serviceLocator; + + /** + * service registrator. + */ + private ServiceRegistrator registrator; + + /** + * service profile resource local cache. + */ + private OpaqueResource serviceProfile; + + /** + * if true the service profile schema will be always updated at boot. + */ + private boolean schemaUpdate = true; + + /** + * disable the service registration. + */ + private boolean disabled = false; + + /** + * registration state of the service profile. + */ + private State state = State.UNKNOWN; + + /** + * check if we have already a registered service profile for this service. + */ + void checkExisting() { + final String uri = registrator.getEprBuilder().getAddress(endpoint) + "?wsdl"; + final String query = "for $x in //RESOURCE_PROFILE[.//RESOURCE_URI/@value='" + uri + "']" + + " where contains($x//RESOURCE_TYPE/@value/string(), 'Service') return $x"; + + try { + final OpaqueResource resource = new StringOpaqueResource(serviceLocator.getService(ISLookUpService.class, true).getResourceProfileByQuery(query)); + + if ("PendingServiceResources".equals(resource.getResourceKind())) { + state = State.PENDING; + } else { + setProfileId(resource.getResourceId()); + setServiceProfile(resource); + state = State.REGISTERED; + } + } catch (final ISLookUpDocumentNotFoundException e) { + log.debug("there is no service registered for uri: " + uri); + state = State.UNREGISTERED; + } catch (final ISLookUpException e) { + log.warn(ERROR_CHECK, e); + } catch (final XPathExpressionException e) { + log.warn(ERROR_CHECK, e); + } catch (final SAXException e) { + log.warn(ERROR_CHECK, e); + } catch (final IOException e) { + log.warn(ERROR_CHECK, e); + } catch (final ParserConfigurationException e) { + log.warn(ERROR_CHECK, e); + } + } + + /** + * check that a pending service profile has been validated. + * + * if it has been validated change the state to REGISTERED. + */ + void checkPending() { + log.debug("checking pending status: " + getService()); + + final String uri = serviceProfile.getResourceUri(); + final String query = "collection('')//RESOURCE_PROFILE[.//RESOURCE_URI/@value='" + uri + "']"; + try { + final OpaqueResource resource = new StringOpaqueResource(serviceLocator.getService(ISLookUpService.class, true).getResourceProfileByQuery(query)); + + if (!"PendingServiceResources".equals(resource.getResourceKind())) { + setProfileId(resource.getResourceId()); + setServiceProfile(resource); + state = State.REGISTERED; + } + } catch (final ISLookUpDocumentNotFoundException e) { + log.debug(PROFILE_NOT_FOUND, e); + } catch (final ISLookUpException e) { + log.warn(ERROR_CHECK, e); + } catch (final XPathExpressionException e) { + log.warn(ERROR_CHECK, e); + } catch (final SAXException e) { + log.warn(ERROR_CHECK, e); + } catch (final IOException e) { + log.warn(ERROR_CHECK, e); + } catch (final ParserConfigurationException e) { + log.warn(ERROR_CHECK, e); + } + } + + /** + * ensure the service is registered. + */ + public void registerService() { + log.debug("registering service profile: " + getService()); + + setProfileId(getRegistrator().registerService(getService(), getEndpoint())); + + if (getProfileId() == null) { + log.debug("cannot register profile, no hnm"); + return; + } + + log.debug("Service profile registered: " + getProfileId()); + + retrieveServiceProfile(); + state = State.PENDING; + if (getProfileId() == null) throw new IllegalStateException("cannot be true, I'm dreaming"); + } + + /** + * register service schema. + */ + public void registerSchema() { + getRegistrator().registerServiceSchema(getService()); + } + + /** + * maintain the cached service profile. + */ + private void retrieveServiceProfile() { + log.debug("Retrieving service profile: " + getProfileId()); + + try { + setServiceProfile(new StringOpaqueResource(serviceLocator.getService(ISLookUpService.class, true).getResourceProfile(getProfileId()))); + } catch (final ISLookUpDocumentNotFoundException e) { + log.debug(PROFILE_NOT_FOUND, e); + } catch (final ISLookUpException e) { + log.warn(ERROR_CHECK, e); + } catch (final XPathExpressionException e) { + log.warn(ERROR_CHECK, e); + } catch (final SAXException e) { + log.warn(ERROR_CHECK, e); + } catch (final IOException e) { + log.warn(ERROR_CHECK, e); + } catch (final ParserConfigurationException e) { + log.warn(ERROR_CHECK, e); + } + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.registration.ServiceRegistrationManager#getServiceProfile() + */ + @Override + public OpaqueResource getServiceProfile() { + retrieveServiceProfile(); + return serviceProfile; + } + + /** + * called periodically to advance the state machine. + */ + public void tick() { + if (disabled) return; + + synchronized (registrator) { + if (state != REGISTERED) { + log.debug("checking service profile registration: " + getService() + " (" + state + ")"); + } + + if (state == UNKNOWN) { + if (schemaUpdate) { + registerSchema(); + } + checkExisting(); + } else if (state == UNREGISTERED) { + registerService(); + } else if (state == PENDING) { + checkPending(); + } + + if (state != REGISTERED) { + log.debug("tick finished"); + } + } + } + + public void setProfileId(final String profileId) { + this.profileId = profileId; + } + + public String getProfileId() { + return profileId; + } + + @Override + public Object getService() { + return service; + } + + @Override + public void setService(final Object service) { + this.service = service; + } + + public Endpoint getEndpoint() { + return endpoint; + } + + @Override + public void setEndpoint(final Endpoint endpoint) { + this.endpoint = endpoint; + } + + public ServiceRegistrator getRegistrator() { + return registrator; + } + + @Required + public void setRegistrator(final ServiceRegistrator registrator) { + this.registrator = registrator; + } + + public void setServiceProfile(final OpaqueResource serviceProfile) { + this.serviceProfile = serviceProfile; + } + + public State getState() { + return state; + } + + public void setState(final State state) { + this.state = state; + } + + public boolean isSchemaUpdate() { + return schemaUpdate; + } + + public void setSchemaUpdate(final boolean schemaUpdate) { + this.schemaUpdate = schemaUpdate; + } + + public boolean isDisabled() { + return disabled; + } + + @Override + public void setDisabled(final boolean disabled) { + this.disabled = disabled; + } + + public UniqueServiceLocator getServiceLocator() { + return serviceLocator; + } + + @Required + public void setServiceLocator(UniqueServiceLocator serviceLocator) { + this.serviceLocator = serviceLocator; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceRegistrator.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceRegistrator.java new file mode 100644 index 0000000..1ca7fe2 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ServiceRegistrator.java @@ -0,0 +1,331 @@ +package eu.dnetlib.enabling.tools.registration; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.transform.dom.DOMResult; +import javax.xml.ws.Endpoint; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.antlr.stringtemplate.StringTemplate; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpDocumentNotFoundException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException; +import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService; +import eu.dnetlib.enabling.locators.UniqueServiceLocator; +import eu.dnetlib.enabling.tools.HNMLocator; +import eu.dnetlib.enabling.tools.NullHNMLocator; +import eu.dnetlib.soap.EndpointReferenceBuilder; + +/** + * This class takes care of registering a service. + * + * TODO: merge the implementation + * + * @author marko + * + */ +public class ServiceRegistrator { + + /** + * second. + */ + private static final int SECOND = 0; + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(ServiceRegistrator.class); // NOPMD + // by + // marko + // on + // 11/24/08 + // 5:02 + // PM + + /** + * locator. + */ + private UniqueServiceLocator serviceLocator; + + /** + * epr builder. + */ + private EndpointReferenceBuilder eprBuilder; + + /** + * component which finds an hnm profile. + */ + private HNMLocator hnmLocator = new NullHNMLocator(); + + /** + * Key-value pairs with service specific parameters which will appear in service profile. + */ + private Map serviceProperties = new HashMap(); + + /** + * Key-value pairs [name - endpoint] of extra protocols available in service profiles. + */ + private Map extraProtocols = new HashMap(); + + /** + * this bean helps us resolve service names from service instances. + */ + private ServiceNameResolver serviceNameResolver = new InterfaceServiceNameResolver(); // NOPMD + + /** + * Register a service schema for a given service. + * + * @param service + * service + */ + public void registerServiceSchema(final Object service) { + registerServiceSchema(serviceNameResolver.getName(service)); + } + + /** + * Register a service schema for a particular service. + * + * @param serviceName + * service name + */ + public void registerServiceSchema(final String serviceName) { + try { + final InputStream schemaStream = getClass().getResourceAsStream("ServiceProfileSchemaTemplate.st"); + if (schemaStream == null) { throw new IllegalStateException("cannot find service profile schema template"); } + + final StringWriter schemaBuffer = new StringWriter(); + IOUtils.copy(schemaStream, schemaBuffer); + final StringTemplate schema = new StringTemplate(schemaBuffer.toString()); + + final String resourceType = serviceName + "ResourceType"; + schema.setAttribute("resourceType", resourceType); + + if (serviceLocator == null) { + log.error("************* SERVICE LOCATOR IS NULL:" + serviceName); + return; + } + final ISRegistryService registry = serviceLocator.getService(ISRegistryService.class, true); + if (registry == null) { + log.error("************* REGISTRY SERVICE IS NULL"); + return; + } + + registry.addResourceType(resourceType, schema.toString()); + } catch (final ISRegistryException e) { + throw new IllegalStateException("cannot register service profile schema", e); + } catch (final IOException e) { + throw new IllegalStateException("cannot read service profile schema template", e); + } + } + + /** + * register a given service. + * + * @param service + * infers the service name from the class or annotations + * @param endpoint + * jaxws endpoint + * @return service profile id + */ + public String registerService(final Object service, final Endpoint endpoint) { + return registerService(serviceNameResolver.getName(service), endpoint); + } + + /** + * register a service with a given service name. Return null if the service cannot be registered, for example because it's blocked if + * the HNM is missing. + * + * @param serviceName + * service name + * @param endpoint + * jaxws endpoint + * @return service profile id, or null if the service cannot be registered because blocked + */ + public String registerService(final String serviceName, final Endpoint endpoint) { + return registerService(serviceName, eprBuilder.getEndpointReference(endpoint)); + } + + /** + * register a service with a given service name. + * + * @param serviceName + * service name + * @param epr + * w3c endpoint reference + * @return service profile id + */ + public String registerService(final String serviceName, final W3CEndpointReference epr) { + + ensureSchemaExists(serviceName); + + final DOMResult result = new DOMResult(); + epr.writeTo(result); + + try { + final InputStream templateStream = getClass().getResourceAsStream("ServiceProfileTemplate.st"); + if (templateStream == null) { throw new IllegalStateException("cannot find service profile template"); } + + final StringWriter buffer = new StringWriter(); + IOUtils.copy(templateStream, buffer); + + final StringTemplate templ = new StringTemplate(buffer.toString()); + + final String resourceType = serviceName + "ResourceType"; + + final String address = result.getNode().getChildNodes().item(0).getChildNodes().item(0).getTextContent(); + final String hnmId = hnmLocator.getHNMForUrl(address); + + // skip registration if there is no HNM yet. + if (hnmId == null) { + log.warn(String.format("skipping %s service registration, can't find NHM service'", serviceName)); + return null; + } + + templ.setAttribute("resourceType", resourceType); + templ.setAttribute("serviceName", serviceName); + templ.setAttribute("address", address); + templ.setAttribute("protocols", getExtraProtocols()); + templ.setAttribute("parentId", hnmId); + templ.setAttribute("properties", serviceProperties); + + log.debug("template: " + templ.toString()); + + final String res = serviceLocator.getService(ISRegistryService.class, true).insertProfileForValidation(resourceType, templ.toString()); + Thread.sleep(SECOND); + return res; + } catch (final IOException e) { + throw new IllegalStateException("cannot load service profile template", e); + + } catch (final ISRegistryException e) { + throw new IllegalStateException("cannot register service profile", e); + } catch (final InterruptedException e) { + throw new IllegalStateException("cannot wait for register service profile", e); + } + } + + /** + * Check that the service schema for this service already exists, and create it if it doesn't. + * + * @param serviceName + * service name + */ + protected void ensureSchemaExists(final String serviceName) { + try { + serviceLocator.getService(ISLookUpService.class).getResourceTypeSchema(serviceName); + //serviceLocator.getService(ISLookUpService.class).getResourceProfileByQuery( + // "//*[local-name() = 'complexType' and @name = 'RESOURCE_TYPEType']//*[local-name() = 'enumeration' and @value = '" + serviceName + // + "ResourceType']"); + log.warn("schema for " + serviceName + " appears to exist"); + } catch (final ISLookUpDocumentNotFoundException e) { + log.warn("registering schema for " + serviceName); + registerServiceSchema(serviceName); + } catch (final ISLookUpException e) { + throw new IllegalStateException(e); + } + } + + /** + * validate the registered profile. + * + * TODO: XXX: huge hack. used right now to automatically register the blackboard. It won't work anymore once the services are validated + * from the GUI + * + * @param profId + * profile id + * @param endpoint + * service endpoint (hook for possible IS_SN automated registrations) + * @return new id. + */ + public String validateProfile(final String profId, final Endpoint endpoint) { + try { + Thread.sleep(SECOND); + } catch (final InterruptedException e) { + throw new IllegalStateException("interrupted", e); + } + + try { + return serviceLocator.getService(ISRegistryService.class, true).validateProfile(profId); + } catch (final ISRegistryException e) { + throw new IllegalStateException("cannot validate service profile", e); + } + } + + /** + * validate the registered profile. + * + * @param profId + * old profile identifier + * @return new profile identifier + */ + public String validateProfile(final String profId) { + return validateProfile(profId, null); + } + + public EndpointReferenceBuilder getEprBuilder() { + return eprBuilder; + } + + @Required + public void setEprBuilder(final EndpointReferenceBuilder eprBuilder) { + this.eprBuilder = eprBuilder; + } + + public HNMLocator getHnmLocator() { + return hnmLocator; + } + + public void setHnmLocator(final HNMLocator hnmLocator) { + this.hnmLocator = hnmLocator; + } + + public ServiceNameResolver getServiceNameGen() { + return serviceNameResolver; + } + + public void setServiceNameGen(final ServiceNameResolver serviceNameGen) { + this.serviceNameResolver = serviceNameGen; + } + + public ServiceNameResolver getServiceNameResolver() { + return serviceNameResolver; + } + + public void setServiceNameResolver(final ServiceNameResolver serviceNameRes) { + this.serviceNameResolver = serviceNameRes; + } + + public Map getServiceProperties() { + return serviceProperties; + } + + public void setServiceProperties(final Map serviceProperties) { + this.serviceProperties = serviceProperties; + } + + public Map getExtraProtocols() { + return extraProtocols; + } + + public void setExtraProtocols(final Map extraProtocols) { + this.extraProtocols = extraProtocols; + } + + public UniqueServiceLocator getServiceLocator() { + return serviceLocator; + } + + @Required + public void setServiceLocator(final UniqueServiceLocator serviceLocator) { + this.serviceLocator = serviceLocator; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/SimpleServiceNameResolver.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/SimpleServiceNameResolver.java new file mode 100644 index 0000000..c961e9f --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/SimpleServiceNameResolver.java @@ -0,0 +1,32 @@ +package eu.dnetlib.enabling.tools.registration; + +/** + * Dummy service name generator implementation. Simply returns the class name. This will never work in practice because + * people have freedom calling their implementations of the service interface as they want. + * + * @author marko + * + */ +@Deprecated +public class SimpleServiceNameResolver implements ServiceNameResolver { + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.registration.ServiceNameResolver#getName(java.lang.Object) + */ + @Override + public String getName(final Object service) { + return getName(service.getClass()); + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.tools.registration.ServiceNameResolver#getName(java.lang.Class) + */ + @Override + public String getName(final Class iface) { + return iface.getSimpleName(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/StaticServiceNameResolver.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/StaticServiceNameResolver.java new file mode 100644 index 0000000..b9e180b --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/StaticServiceNameResolver.java @@ -0,0 +1,43 @@ +package eu.dnetlib.enabling.tools.registration; + +/** + * Useful if you want to register a service external to this framework. + * + * @author marko + * + */ +@Deprecated +public class StaticServiceNameResolver implements ServiceNameResolver { + + /** + * service name. + */ + private String name; + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.tools.registration.ServiceNameResolver#getName(java.lang.Object) + */ + @Override + public String getName(final Object service) { + return name; + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.tools.registration.ServiceNameResolver#getName(java.lang.Class) + */ + @Override + public String getName(final Class serviceInterface) { + return name; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ValidatingServiceRegistrationManagerImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ValidatingServiceRegistrationManagerImpl.java new file mode 100644 index 0000000..a263e72 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/enabling/tools/registration/ValidatingServiceRegistrationManagerImpl.java @@ -0,0 +1,62 @@ +package eu.dnetlib.enabling.tools.registration; + +import static eu.dnetlib.enabling.tools.registration.ServiceRegistrationManagerImpl.State.PENDING; +import static eu.dnetlib.enabling.tools.registration.ServiceRegistrationManagerImpl.State.REGISTERED; + +/** + * This service registration manager implementation performs automatic service profile validation. + *

+ * This is useful during automated testing. You can use it simply by overriding the default class in the service + * registration manager bean template: + *

+ * + *
+ * 	<template:instance name="serviceRegistrationManager"
+ *  t:serviceRegistrationManagerClass="eu.dnetlib.enabling.tools.registration.ValidatingServiceRegistrationManagerImpl"
+ *  t:name="myServiceRegistrationManager" t:service="myService" t:endpoint="myServiceEndpoint"
+ *  t:jobScheduler="jobScheduler"/>
+ * 
+ * + *

+ * If your service needs to receive blackboard messages, the notification can be automatically subscribed to your + * service profile simply by using a different service registrator component (blackboardServiceRegistrator): + *

+ * + *
+ * 	<template:instance name="serviceRegistrationManager"
+ *  t:serviceRegistrationManagerClass="eu.dnetlib.enabling.tools.registration.ValidatingServiceRegistrationManagerImpl"
+ *  t:name="myServiceRegistrationManager" t:service="myService" t:endpoint="myServiceEndpoint"
+ *  t:jobScheduler="jobScheduler" t:serviceRegistrator="blackboardServiceRegistrator"/>
+ * 
+ * + *

+ * This option is very useful for example to the MDStoreService or the IndexService. + *

+ * + * @author marko + * + */ +public class ValidatingServiceRegistrationManagerImpl extends ServiceRegistrationManagerImpl { + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.registration.ServiceRegistrationManagerImpl#tick() + */ + @Override + public void tick() { + synchronized (getRegistrator()) { + if (getState() == PENDING) { + if(getProfileId() == null) { + throw new IllegalStateException("State is PENDING but profile id isn't initialized"); + } + final String newId = getRegistrator().validateProfile(getProfileId(), getEndpoint()); + setProfileId(newId); + setState(REGISTERED); + return; + } + } + + super.tick(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CQLExpander.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CQLExpander.java new file mode 100644 index 0000000..eebe7b5 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CQLExpander.java @@ -0,0 +1,61 @@ +package eu.dnetlib.functionality.cql; + +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.z3950.zing.cql.CQLBooleanNode; +import org.z3950.zing.cql.CQLNode; +import org.z3950.zing.cql.CQLOrNode; +import org.z3950.zing.cql.CQLParser; +import org.z3950.zing.cql.CQLTermNode; +import org.z3950.zing.cql.ModifierSet; + +public class CQLExpander { + + private static final String OP = "or"; + + private static final String _OP_ = " " + OP + " "; + + public CQLNode expand(CQLNode node, Set fields) { + return doExpand(node, fields); + } + + private static CQLNode doExpand(CQLNode node, Set fields) { + + if (node instanceof CQLBooleanNode) { + return doExpand((CQLBooleanNode) node, fields); + } + + if (node instanceof CQLTermNode) { + return doExpand((CQLTermNode) node, fields); + } + + // if (node == null) + // return terms; + + throw new RuntimeException("error choice"); + } + + private static CQLNode doExpand(CQLBooleanNode node, Set fields) { + + CQLNode left = doExpand(node.left, fields); + CQLNode right = doExpand(node.right, fields); + + return new CQLOrNode(left, right, new ModifierSet("or")); + } + + private static CQLNode doExpand(CQLTermNode node, Set fields) { + String expand = ""; + for (String field : fields) { + expand += field + "=" + node.getTerm() + _OP_; + } + + expand = StringUtils.removeEnd(expand, _OP_); + try { + return new CQLParser().parse(expand); + } catch (Exception e) { + throw new RuntimeException("unable to parse: " + expand); + } + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CQLFieldLister.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CQLFieldLister.java new file mode 100644 index 0000000..d17bfdb --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CQLFieldLister.java @@ -0,0 +1,49 @@ +package eu.dnetlib.functionality.cql; + +import java.util.HashSet; +import java.util.Set; + +import org.z3950.zing.cql.CQLBooleanNode; +import org.z3950.zing.cql.CQLNode; +import org.z3950.zing.cql.CQLTermNode; + +public class CQLFieldLister { + + public Set listFields(final CQLNode node) { + return doFilter(node, new HashSet()); + } + + private Set doFilter(CQLNode node, Set fields) { + + if (node instanceof CQLBooleanNode) { + return doFilter((CQLBooleanNode) node, fields); + } + + if (node instanceof CQLTermNode) { + return doFilter((CQLTermNode) node, fields); + } + + if (node == null) { + return fields; + } + + throw new RuntimeException("error choice"); + } + + private Set doFilter(CQLBooleanNode node, Set fields) { + + Set left = doFilter(node.left, fields); + Set right = doFilter(node.right, fields); + + left.addAll(right); + + return left; + } + + private static Set doFilter(CQLTermNode node, Set terms) { + + terms.add(node.getIndex()); + return terms; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlFilter.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlFilter.java new file mode 100644 index 0000000..3e31204 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlFilter.java @@ -0,0 +1,80 @@ +package eu.dnetlib.functionality.cql; + +import java.util.List; + +import org.z3950.zing.cql.CQLAndNode; +import org.z3950.zing.cql.CQLBooleanNode; +import org.z3950.zing.cql.CQLNode; +import org.z3950.zing.cql.CQLNotNode; +import org.z3950.zing.cql.CQLOrNode; +import org.z3950.zing.cql.CQLPrefixNode; +import org.z3950.zing.cql.CQLTermNode; + +public class CqlFilter { + + public CQLNode filter(final CQLNode node, final List fields) { + return doFilter(node, fields); + } + + private CQLNode doFilter(CQLNode node, List fields) { + + if (node instanceof CQLBooleanNode) { + return doFilter((CQLBooleanNode) node, fields); + } + + if (node instanceof CQLTermNode) { + return doFilter((CQLTermNode) node, fields); + } + + if (node instanceof CQLPrefixNode) { + return node; + } + + if (node == null) { + return null; + } + + throw new RuntimeException("error choice"); + } + + private CQLNode doFilter(CQLBooleanNode node, List fields) { + + CQLNode left = doFilter(node.left, fields); + CQLNode right = doFilter(node.right, fields); + + if (left == null && right == null) { + return null; + } + + if (left == null) { + return right; + } + + if (right == null) { + return left; + } + + if (node instanceof CQLAndNode) { + return new CQLAndNode(left, right, node.ms); + } + + if (node instanceof CQLOrNode) { + return new CQLOrNode(left, right, node.ms); + } + + if (node instanceof CQLNotNode) { + return new CQLNotNode(left, right, node.ms); + } + + throw new RuntimeException("unknow boolean node"); + } + + private static CQLNode doFilter(CQLTermNode node, List fields) { + return isTermNodeToFilter(node, fields) ? null : node; + } + + private static boolean isTermNodeToFilter(CQLTermNode node, List fields) { + return fields.contains(node.getIndex().toLowerCase()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlGroup.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlGroup.java new file mode 100644 index 0000000..63a4f55 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlGroup.java @@ -0,0 +1,71 @@ +package eu.dnetlib.functionality.cql; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.z3950.zing.cql.CQLAndNode; +import org.z3950.zing.cql.CQLBooleanNode; +import org.z3950.zing.cql.CQLNode; +import org.z3950.zing.cql.CQLPrefixNode; +import org.z3950.zing.cql.CQLTermNode; +import org.z3950.zing.cql.ModifierSet; + +public class CqlGroup { + + public static final String defaultTerm = "cql.filtered"; + + public Map group(final CQLNode node, final List fields) { + return node != null ? doGroup(node, fields, new HashMap()) : new HashMap(); + } + + private Map doGroup(CQLNode node, List fields, Map groups) { + + if (node instanceof CQLBooleanNode) { + return doGroup((CQLBooleanNode) node, fields, groups); + } + + if (node instanceof CQLTermNode) { + return doGroup((CQLTermNode) node, fields, groups); + } + + if (node instanceof CQLPrefixNode) { + return groups; + } + + throw new RuntimeException("error choice"); + } + + private Map doGroup(CQLBooleanNode node, List fields, Map groups) { + + doGroup(node.left, fields, groups); + doGroup(node.right, fields, groups); + + return groups; + } + + private static Map doGroup(CQLTermNode node, List fields, Map groups) { + + if (isTermNodeToGroup(node, fields)) { + String term = node.getIndex().toLowerCase(); + + if (groups.get(term) == null) { + groups.put(term, node); + } + + CQLNode group = groups.get(term); + CQLAndNode andNode = new CQLAndNode(group, node, new ModifierSet("and")); + + if (!groups.containsValue(node)) { + groups.put(term, andNode); + } + } + + return groups; + } + + private static boolean isTermNodeToGroup(CQLTermNode node, List fields) { + return fields.contains(node.getIndex().toLowerCase()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlTermLister.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlTermLister.java new file mode 100644 index 0000000..84c837d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlTermLister.java @@ -0,0 +1,55 @@ +package eu.dnetlib.functionality.cql; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.z3950.zing.cql.CQLBooleanNode; +import org.z3950.zing.cql.CQLNode; +import org.z3950.zing.cql.CQLTermNode; + +import com.google.common.collect.Lists; + +public class CqlTermLister { + + public List listTerms(final CQLNode node, final String field) { + return Lists.newArrayList(doListTerms(node, field, new HashSet())); + } + + private Set doListTerms(CQLNode node, String field, Set terms) { + + if (node instanceof CQLBooleanNode) { + return doListTerms((CQLBooleanNode) node, field, terms); + } + + if (node instanceof CQLTermNode) { + return doListTerms((CQLTermNode) node, field, terms); + } + + if (node == null) { + return terms; + } + + throw new RuntimeException("error choice"); + } + + private Set doListTerms(CQLBooleanNode node, String field, Set terms) { + + Set left = doListTerms(node.left, field, terms); + Set right = doListTerms(node.right, field, terms); + + left.addAll(right); + + return left; + } + + private static Set doListTerms(CQLTermNode node, String field, Set terms) { + + if (field.equalsIgnoreCase(node.getIndex())) { + terms.add(node.getTerm()); + } + + return terms; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlTranslator.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlTranslator.java new file mode 100644 index 0000000..6c6fa1a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlTranslator.java @@ -0,0 +1,198 @@ +package eu.dnetlib.functionality.cql; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import eu.dnetlib.functionality.cql.lucene.TranslatedQuery; +import org.bson.conversions.Bson; +import org.z3950.zing.cql.CQLNode; +import org.z3950.zing.cql.CQLParseException; + +import com.google.common.collect.BiMap; + +public interface CqlTranslator { + + /** + * main translator method + * + * @param queryRoot + * String representation of the cql query + * @param options + * options that modify the behavior of cql parsing + * @return String representing the query in the lucene syntax + * @throws CQLParseException + * @throws IOException + */ + public String toLucene(final String queryRoot) throws CQLParseException, IOException; + + /** + * main translator method + * + * @param queryRoot + * String representation of the cql query + * @return String representing the query in the lucene syntax + * @throws CQLParseException + * @throws IOException + */ + public String toLucene(final String queryRoot, Map> options) throws CQLParseException, IOException; + + /** + * main translator method + * + * @param queryRoot + * String representation of the cql query + * @param valueTransformerMap + * callbacks which allow us to normalize field values when we know about them + * @return String representing the query in the lucene syntax + * @throws CQLParseException + * @throws IOException + */ + public String toLucene(final String queryRoot, CqlValueTransformerMap valueTransformerMap) throws CQLParseException, IOException; + + /** + * main translator method + * + * @param queryRoot + * Pre-parsed representation of the cql query + * @return String representing the query in the lucene syntax + * @throws CQLParseException + * @throws IOException + */ + public String toLucene(final CQLNode queryRoot) throws CQLParseException, IOException; + + /** + * main translator method + * + * @param queryRoot + * Pre-parsed representation of the cql query + * @param valueTransformerMap + * callbacks which allow us to normalize field values when we know about them + * @return String representing the query in the lucene syntax + * @throws CQLParseException + * @throws IOException + */ + public String toLucene(final CQLNode queryRoot, CqlValueTransformerMap valueTransformerMap) throws CQLParseException, IOException; + + /** + * main translator method + * + * @param queryRoot + * Pre-parsed representation of the cql query + * @param valueTransformerMap + * callbacks which allow us to normalize field values when we know about them + * @param options + * options that modify the behavior of cql parsing + * @return String representing the query in the lucene syntax + * @throws CQLParseException + * @throws IOException + */ + public String toLucene(final CQLNode queryRoot, CqlValueTransformerMap valueTransformerMap, Map> options) + throws CQLParseException, IOException; + + /** + * method performs the translation and returns a TranslatedQuery object which contains the translated query and the + * cql-related options. + * + * @param queryRoot + * Pre-parsed representation of the cql query + * @param valueTransformerMap + * callbacks which allow us to normalize field values when we know about them + * @return TranslatedQuery object which contains the translated query and the cql-related options + * @throws CQLParseException + * @throws IOException + */ + public TranslatedQuery getTranslatedQuery(final String cqlQuery) throws CQLParseException, IOException; + + /** + * method performs the translation and returns a TranslatedQuery object which contains the translated query and the + * cql-related options. + * + * @param queryRoot + * Pre-parsed representation of the cql query + * @param valueTransformerMap + * callbacks which allow us to normalize field values when we know about them + * @return TranslatedQuery object which contains the translated query and the cql-related options + * @throws CQLParseException + * @throws IOException + */ + public TranslatedQuery getTranslatedQuery(final CQLNode queryRoot, CqlValueTransformerMap valueTransformerMap) throws CQLParseException, IOException; + + /** + * method performs the translation and returns a TranslatedQuery object which contains the translated query and the + * cql-related options. + * + * @param queryRoot + * Pre-parsed representation of the cql query + * @param valueTransformerMap + * callbacks which allow us to normalize field values when we know about them + * @param options + * options that modify the behavior of cql parsing + * @return TranslatedQuery object which contains the translated query and the cql-related options + * @throws CQLParseException + * @throws IOException + */ + public TranslatedQuery getTranslatedQuery( + final CQLNode queryRoot, + CqlValueTransformerMap valueTransformerMap, + Map> options, + BiMap aliases, + Map weights) throws CQLParseException, IOException; + + /** + * method performs the translation and returns a TranslatedQuery object which contains the translated query and the + * cql-related options. + * + * @param queryRoot + * Pre-parsed representation of the cql query + * @param valueTransformerMap + * callbacks which allow us to normalize field values when we know about them + * @param options + * options that modify the behavior of cql parsing + * @return TranslatedQuery object which contains the translated query and the cql-related options + * @throws CQLParseException + * @throws IOException + */ + public TranslatedQuery getTranslatedQuery(final String cqlQuery, Map> options) throws CQLParseException, IOException; + + /** + * method performs the translation and returns a TranslatedQuery object which contains the translated query and the + * cql-related options. + * + * @param queryRoot + * Pre-parsed representation of the cql query + * @param valueTransformerMap + * callbacks which allow us to normalize field values when we know about them + * @return TranslatedQuery object which contains the translated query and the cql-related options + * @throws CQLParseException + * @throws IOException + */ + public TranslatedQuery getTranslatedQuery(final String cqlQuery, CqlValueTransformerMap valueTransformerMap) throws CQLParseException, IOException; + + /** + * method performs the translation and returns a TranslatedQuery object which contains the translated query and the + * cql-related options. + * + * @param queryRoot + * Pre-parsed representation of the cql query + * @param valueTransformerMap + * callbacks which allow us to normalize field values when we know about them + * @param options + * options that modify the behavior of cql parsing + * @return TranslatedQuery object which contains the translated query and the cql-related options + * @throws CQLParseException + * @throws IOException + */ + public TranslatedQuery getTranslatedQuery(final String cqlQuery, CqlValueTransformerMap valueTransformerMap, Map> options) + throws CQLParseException, IOException; + + /** + * Translates a cql query in a mongodb query. + * @param cqlQuery + * the query to be translated. + * @return + * a mongodb query expressed as org.bson.conversions.Bson object. + */ + public Bson toMongo(final String cqlQuery) throws IOException, CQLParseException; + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlTranslatorImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlTranslatorImpl.java new file mode 100644 index 0000000..697f1e7 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlTranslatorImpl.java @@ -0,0 +1,136 @@ +package eu.dnetlib.functionality.cql; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import eu.dnetlib.functionality.cql.lucene.IdentityCqlValueTransformerMap; +import eu.dnetlib.functionality.cql.lucene.LuceneCqlTranslator; +import eu.dnetlib.functionality.cql.lucene.TranslatedQuery; +import eu.dnetlib.functionality.cql.mongo.MongoCqlTranslator; +import org.bson.conversions.Bson; +import org.z3950.zing.cql.CQLNode; +import org.z3950.zing.cql.CQLParseException; +import org.z3950.zing.cql.CQLParser; + +/** + * This class provides a simple cql-to-lucene translator + * + * TODO implement translation distinguishing org.apache.lucene.search.TermRangeQuery and + * org.apache.lucene.search.NumericRangeQuery + * + * @author claudio + * + */ +public class CqlTranslatorImpl implements CqlTranslator { + + /** + * {@link CqlTranslator#toLucene(String)} + */ + @Override + public String toLucene(final String queryRoot) throws CQLParseException, IOException { + return toLucene(parse(queryRoot), new IdentityCqlValueTransformerMap()); + } + + @Override + public String toLucene(final String queryRoot, Map> options) throws CQLParseException, IOException { + return toLucene(parse(queryRoot), new IdentityCqlValueTransformerMap(), options); + } + + @Override + public String toLucene(final String queryRoot, CqlValueTransformerMap valueTransformerMap) throws CQLParseException, IOException { + return toLucene(parse(queryRoot), valueTransformerMap); + } + + @Override + public String toLucene(final CQLNode queryRoot) throws CQLParseException, IOException { + return toLucene(queryRoot, new IdentityCqlValueTransformerMap()); + } + + @Override + public String toLucene(final CQLNode queryRoot, CqlValueTransformerMap valueTransformerMap) throws CQLParseException, IOException { + + return getTranslatedQuery(queryRoot, valueTransformerMap).asLucene(); + } + + @Override + public String toLucene(final CQLNode queryRoot, CqlValueTransformerMap valueTransformerMap, Map> options) + throws CQLParseException, IOException { + final BiMap aliases = HashBiMap.create(); + return getTranslatedQuery(queryRoot, valueTransformerMap, options, aliases, new HashMap()).asLucene(); + } + + @Override + public TranslatedQuery getTranslatedQuery(final CQLNode queryRoot, CqlValueTransformerMap valueTransformerMap) throws CQLParseException, IOException { + + final BiMap aliases = HashBiMap.create(); + return getTranslatedQuery(queryRoot, valueTransformerMap, new HashMap>(), aliases, new HashMap()); + } + + @Override + public TranslatedQuery getTranslatedQuery( + final CQLNode queryRoot, + CqlValueTransformerMap valueTransformerMap, + Map> options, + BiMap aliases, + Map weights) throws CQLParseException, IOException { + + return LuceneCqlTranslator.translate(hackAnd(queryRoot), valueTransformerMap, options, aliases, weights); + } + + // hack, to be removed soon + private CQLNode hackAnd(final CQLNode queryRoot) throws CQLParseException, IOException { + String cql = queryRoot.toCQL().toLowerCase(); + + if (cql.startsWith("\"and ") || cql.startsWith("and ")) { + return parse(cql.replaceFirst("and ", "")); + } + return queryRoot; + } + + @Override + public TranslatedQuery getTranslatedQuery(String cqlQuery) throws CQLParseException, IOException { + return getTranslatedQuery(parse(cqlQuery), new IdentityCqlValueTransformerMap()); + } + + @Override + public TranslatedQuery getTranslatedQuery(String cqlQuery, Map> options) throws CQLParseException, IOException { + final BiMap aliases = HashBiMap.create(); + return getTranslatedQuery(parse(cqlQuery), new IdentityCqlValueTransformerMap(), options, aliases, new HashMap()); + } + + @Override + public TranslatedQuery getTranslatedQuery(String cqlQuery, CqlValueTransformerMap valueTransformerMap) throws CQLParseException, IOException { + return getTranslatedQuery(parse(cqlQuery), valueTransformerMap); + } + + @Override + public TranslatedQuery getTranslatedQuery(String cqlQuery, CqlValueTransformerMap valueTransformerMap, Map> options) + throws CQLParseException, IOException { + final BiMap aliases = HashBiMap.create(); + return getTranslatedQuery(parse(cqlQuery), valueTransformerMap, options, aliases, new HashMap()); + } + + @Override + public Bson toMongo(final String cqlQuery) throws IOException, CQLParseException { + return MongoCqlTranslator.toMongo(cqlQuery); + } + + /** + * helper parsing method + * + * @param query + * @return + * @throws CQLParseException + * @throws IOException + */ + protected static CQLNode parse(final String query) throws CQLParseException, IOException { + return new CQLParser().parse(query); + } + + + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlUtils.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlUtils.java new file mode 100644 index 0000000..39a4728 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlUtils.java @@ -0,0 +1,91 @@ +package eu.dnetlib.functionality.cql; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.z3950.zing.cql.CQLNode; +import org.z3950.zing.cql.CQLOrNode; +import org.z3950.zing.cql.CQLParseException; +import org.z3950.zing.cql.CQLParser; +import org.z3950.zing.cql.ModifierSet; + +import com.google.common.base.Predicates; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +public class CqlUtils { + + public static final String cqlDefaultField = "cql.serverChoice"; + + private CqlUtils() {} + + public static CQLNode expand(final String query, Set fields) { + return expand(parse(query), fields); + } + + public static CQLNode expand(CQLNode root, Set fields) { + + if (fields.isEmpty()) { + return root; + } + + Map unfielded = group(filter(root, listFields(root)), Lists.newArrayList(CqlUtils.cqlDefaultField)); + CQLNode terms = unfielded.get(CqlGroup.defaultTerm); + + if (terms != null) { + CQLNode expand = new CQLExpander().expand(terms, fields); + return new CQLOrNode(root, expand, new ModifierSet("or")); + } + + return root; + } + + public static List listFields(final String query) { + return listFields(parse(query)); + } + + public static List listFields(CQLNode root) { + Set fields = new CQLFieldLister().listFields(root); + fields.remove(cqlDefaultField); + return Lists.newArrayList(fields); + } + + public static List listTerms(final String query, final String field) { + return listTerms(parse(query), field); + } + + public static List listTerms(final CQLNode query, final String field) { + return new CqlTermLister().listTerms(group(query, Lists.newArrayList(field)).get(field), field); + } + + public static Map group(final CQLNode root, final List fields) { + Map groups = new CqlGroup().group(root, fields); + groups.put(CqlGroup.defaultTerm, new CqlFilter().filter(root, fields)); + return Maps.filterValues(groups, Predicates.notNull()); + } + + public static Map group(final String query, final List fields) { + return group(parse(query), fields); + } + + public static CQLNode filter(final CQLNode query, final List fields) { + return new CqlFilter().filter(query, fields); + } + + public static CQLNode filter(final String query, final List fields) { + return filter(parse(query), fields); + } + + public static CQLNode parse(final String query) { + try { + return query != null ? new CQLParser().parse(query) : null; + } catch (CQLParseException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlValueTransformerMap.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlValueTransformerMap.java new file mode 100644 index 0000000..96fc983 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/CqlValueTransformerMap.java @@ -0,0 +1,21 @@ +package eu.dnetlib.functionality.cql; + +import eu.dnetlib.miscutils.functional.UnaryFunction; + +/** + * Allows indices to register special field-specific value transformations, for example to normalize field values like + * for solr dates for example. + * + * @author marko + * + */ +public interface CqlValueTransformerMap { + /** + * Return a function which normalizes the value. + * + * @param field + * field name + * @return normalized value + */ + UnaryFunction transformerFor(String field); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/IdentityCqlValueTransformerMap.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/IdentityCqlValueTransformerMap.java new file mode 100644 index 0000000..ae60f61 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/IdentityCqlValueTransformerMap.java @@ -0,0 +1,22 @@ +package eu.dnetlib.functionality.cql.lucene; + +import eu.dnetlib.functionality.cql.CqlValueTransformerMap; +import eu.dnetlib.miscutils.functional.IdentityFunction; +import eu.dnetlib.miscutils.functional.UnaryFunction; + +/** + * Backward compatibiltiy implementation, which doesn't do anything to fields. + * + * @author marko + * + */ +public class IdentityCqlValueTransformerMap implements CqlValueTransformerMap { + + private UnaryFunction function = new IdentityFunction(); + + @Override + public UnaryFunction transformerFor(String field) { + return function; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/LuceneCqlTranslator.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/LuceneCqlTranslator.java new file mode 100644 index 0000000..42c6c37 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/LuceneCqlTranslator.java @@ -0,0 +1,147 @@ +package eu.dnetlib.functionality.cql.lucene; + +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import com.google.common.collect.BiMap; +import eu.dnetlib.functionality.cql.CqlValueTransformerMap; +import eu.dnetlib.functionality.cql.lucene.SortOperation.Mode; +import eu.dnetlib.functionality.cql.parse.*; +import eu.dnetlib.miscutils.functional.UnaryFunction; +import org.z3950.zing.cql.*; + +/** + * Created by claudio on 12/09/16. + */ +public class LuceneCqlTranslator { + + /** + * Internal recursive translator method. + * + * @param node + * @param valueTranformerMap + * @param cqlOptions + * @param aliases + * @param weights + * @return + * @throws CQLParseException + */ + public static TranslatedQuery translate( + CQLNode node, + CqlValueTransformerMap valueTranformerMap, + Map> cqlOptions, + BiMap aliases, + Map weights) throws CQLParseException { + + if (node instanceof CQLBooleanNode) { + return doTranslate((CQLBooleanNode) node, valueTranformerMap, cqlOptions, aliases, weights); + } + + if (node instanceof CQLTermNode) { + return doTranslate((CQLTermNode) node, valueTranformerMap, cqlOptions, aliases, weights); + } + + if (node instanceof CQLSortNode) { + return doTranslate((CQLSortNode) node, valueTranformerMap, cqlOptions, aliases, weights); + } + + throw new RuntimeException("error choice"); + + } + + private static TranslatedQuery doTranslate( + CQLSortNode node, + CqlValueTransformerMap valueTranformerMap, + Map> cqlOptions, + BiMap aliases, + Map weights) throws CQLParseException { + + TranslatedQuery subQuery = translate(node.subtree, valueTranformerMap, cqlOptions, aliases, weights); + Node query = subQuery.getQuery(); + + final String sortField = node.getSortIndexes().get(0).getBase(); + final Mode sortMode = SortOperation.modifiersToMode(node.getSortIndexes().get(0).getModifiers()); + + QueryOptions options = new QueryOptions(new SortOperation(sortField, sortMode)); + + return new TranslatedQuery(query, options); + } + + private static TranslatedQuery doTranslate( + CQLBooleanNode node, + CqlValueTransformerMap valueTranformerMap, + Map> cqlOptions, + BiMap aliases, + Map weights) throws CQLParseException { + + TranslatedQuery left = translate(node.left, valueTranformerMap, cqlOptions, aliases, weights); + TranslatedQuery right = translate(node.right, valueTranformerMap, cqlOptions, aliases, weights); + + QueryOptions options = right.getOptions(); + if (options != null) { + options.merge(left.getOptions()); + } + + if (node instanceof CQLAndNode) { + return new TranslatedQuery(new AndNode(left.getQuery(), right.getQuery()), options); + } + + if (node instanceof CQLOrNode) { + return new TranslatedQuery(new OrNode(left.getQuery(), right.getQuery()), options); + } + + if (node instanceof CQLNotNode) { + return new TranslatedQuery(new NotNode(left.getQuery(), right.getQuery()), options); + } + + throw new RuntimeException("unknow boolean node"); + } + + private static TranslatedQuery doTranslate( + CQLTermNode node, + CqlValueTransformerMap valueTranformerMap, + Map> cqlOptions, + BiMap aliases, + Map weights) { + + String rel = node.getRelation().getBase(); + final String index = node.getIndex().toLowerCase(); + final Vector modifiers = node.getRelation().getModifiers(); + + UnaryFunction valueTransformer = valueTranformerMap.transformerFor(index); + + String term = valueTransformer.evaluate(node.getTerm()); + + if (!index.equals("")) { + + if (modifiers.size() > 0) { + rel = modifiers.firstElement().getType(); + } + + if (Relations.contains(rel)) { + return buildTerm(cqlOptions, rel, index, term, aliases, weights); + } + + //anything else is unsupported + throw new RuntimeException("unknow relation: " + rel); + } + + throw new RuntimeException("invalid field: " + index); + } + + private static TranslatedQuery buildTerm( + Map> cqlOptions, + String rel, + String index, + String term, + BiMap aliases, + Map weights) { + + // if (weights.containsKey(index)) { + // term += "^" + weights.get(index); + // } + return new TranslatedQuery(new TermNode(index, Relations.get(rel), term, cqlOptions, aliases, weights), new QueryOptions()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/QueryOptions.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/QueryOptions.java new file mode 100644 index 0000000..9acd1a5 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/QueryOptions.java @@ -0,0 +1,39 @@ +package eu.dnetlib.functionality.cql.lucene; + +public class QueryOptions { + private SortOperation sort; + + public QueryOptions() { + } + + public QueryOptions(SortOperation sort) { + super(); + this.sort = sort; + } + + public void merge(QueryOptions other) { + if (getSort() != null) { + setSort(other.getSort()); + } + } + + public SortOperation getSort() { + return sort; + } + + public void setSort(SortOperation sort) { + this.sort = sort; + } + + public String appendOptions(String baseQuery) { + if (sort != null) { + return baseQuery + "&sort=" + sort.getField() + " " + sort.getMode(); + } + return baseQuery; + } + + @Override + public String toString() { + return sort.getField() + " " + sort.getMode(); + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/SortOperation.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/SortOperation.java new file mode 100644 index 0000000..20d1fc7 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/SortOperation.java @@ -0,0 +1,50 @@ +package eu.dnetlib.functionality.cql.lucene; + +import java.util.Vector; + +import org.z3950.zing.cql.Modifier; + +public class SortOperation { + public enum Mode { + asc, desc + } + + private String field; + private Mode mode; + + public SortOperation(String field, Mode mode) { + super(); + this.field = field; + this.mode = mode; + } + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } + + public Mode getMode() { + return mode; + } + + public void setMode(Mode mode) { + this.mode = mode; + } + + public static Mode modifierToMode(Modifier modifier) { + if ("sort.ascending".equals(modifier.getType())) { + return Mode.asc; + } + return Mode.desc; + } + + public static Mode modifiersToMode(Vector vector) { + if (vector.size() > 0) { + return modifierToMode(vector.firstElement()); + } + return Mode.desc; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/TranslatedQuery.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/TranslatedQuery.java new file mode 100644 index 0000000..a8def53 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/lucene/TranslatedQuery.java @@ -0,0 +1,41 @@ +package eu.dnetlib.functionality.cql.lucene; + +import eu.dnetlib.functionality.cql.parse.Node; + +public class TranslatedQuery { + + /** + * default field name. + */ + private static final String SERVER_CHOICE_FIELD = "cql.serverchoice:"; + + private Node query; + private QueryOptions options; + + public TranslatedQuery(Node query, QueryOptions options) { + super(); + this.query = query; + this.options = options; + } + + public String asLucene() { + return query.toLucene().replace(SERVER_CHOICE_FIELD, "").trim(); + } + + public Node getQuery() { + return query; + } + + public void setQuery(Node query) { + this.query = query; + } + + public QueryOptions getOptions() { + return options; + } + + public void setOptions(QueryOptions options) { + this.options = options; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/mongo/MongoCqlTranslator.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/mongo/MongoCqlTranslator.java new file mode 100644 index 0000000..6d3f0f8 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/mongo/MongoCqlTranslator.java @@ -0,0 +1,238 @@ +package eu.dnetlib.functionality.cql.mongo; + +import java.io.IOException; +import java.util.List; + +import com.google.common.collect.Lists; +import com.mongodb.BasicDBObject; +import com.mongodb.BasicDBObjectBuilder; +import eu.dnetlib.functionality.cql.parse.Relation; +import eu.dnetlib.functionality.cql.parse.Relations; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.ISODateTimeFormat; +import org.z3950.zing.cql.*; + +/** + * Created by claudio on 12/09/16. + */ +public class MongoCqlTranslator { + + private static final Log log = LogFactory.getLog(MongoCqlTranslator.class); // NOPMD by marko on 11/24/08 5:02 PM + + //field names containing dates for the OAI publisher. See also OAICOnfigurationReader constants that are not visible here. + //TODO: Generalize + private final List dateFields = Lists.newArrayList("datestamp", "lastCollectionDate"); + /** + * Parses the given query string into a mongo DBObject. + * + * @param query + * String to parse + * @return DBObject corresponding to the query string + */ + public static Bson toMongo(final String query) throws CQLParseException, IOException { + log.debug("PARSING: " + query); + if (StringUtils.isBlank(query)) return new BasicDBObject(); + final Bson parsed = new MongoCqlTranslator().doParse(query); + log.debug(parsed); + return parsed; + } + + private Bson doParse(final String query) throws IOException, CQLParseException { + CQLParser parser = new CQLParser(); + CQLNode root; + root = parser.parse(query); + return doParse(root); + } + + private Bson doParse(final CQLNode node) throws CQLParseException { + if (node instanceof CQLTermNode) return doTranslate((CQLTermNode) node); + if (node instanceof CQLBooleanNode) return doTranslate((CQLBooleanNode) node); + + throw new RuntimeException("error choice for CQLNode " + node.getClass()); + } + + private Bson doTranslate(final CQLTermNode termNode) throws CQLParseException { + if (termNode.getTerm().equals("*") && (termNode.getIndex().equals("*") || termNode.getIndex().equals("cql.serverChoice"))) return new BasicDBObject(); + String relation = termNode.getRelation().getBase(); + Relation rel = Relations.get(relation); + return this.handleRelationNode(rel, termNode); + } + + private Bson handleRelationNode(final Relation rel, final CQLTermNode termNode) throws CQLParseException { + BasicDBObject mongoQueryObject = new BasicDBObject(); + String term = termNode.getTerm(); + String indexName = termNode.getIndex(); + Object termObj = term; + if (dateFields.contains(indexName)){ + OAIDate termDate = this.parseDate(term); + return handleDateRelationNode(indexName, rel, termDate); + } + if (indexName.equals("_id")) { + termObj = new ObjectId(term); + } + switch (rel) { + case EQUAL: + case EXACT: + if(!term.equals("*")) { + mongoQueryObject.put(indexName, termObj); + } + else{ + //special case to handle exist queries such as fieldname = * --> qty: { $exists: true} + mongoQueryObject.put(indexName, new BasicDBObject("$exists", true)); + } + break; + case NOT: + mongoQueryObject.put(indexName, new BasicDBObject("$ne", termObj)); + break; + case GT: + mongoQueryObject.put(indexName, new BasicDBObject("$gt", termObj)); + break; + case GTE: + mongoQueryObject.put(indexName, new BasicDBObject("$gte", termObj)); + break; + case LT: + mongoQueryObject.put(indexName, new BasicDBObject("$lt", termObj)); + break; + case LTE: + mongoQueryObject.put(indexName, new BasicDBObject("$lte", termObj)); + break; + default: + throw new CQLParseException("Can't parse query: relation " + rel + " not supported!"); + } + return mongoQueryObject; + } + + private Bson doTranslate(final CQLBooleanNode node) throws CQLParseException { + if (node instanceof CQLAndNode) return getBooleanQuery("$and", node); + if (node instanceof CQLOrNode) return getBooleanQuery("$or", node); + if (node instanceof CQLNotNode) return getNotQuery((CQLNotNode) node); + throw new RuntimeException("error choice for CQLBooleanNode " + node.getClass()); + } + + private Bson getBooleanQuery(final String mongoOperator, final CQLBooleanNode node) throws CQLParseException { + Bson left = doParse(node.left); + Bson right = doParse(node.right); + BasicDBObject opQuery = new BasicDBObject(); + List termList = Lists.newArrayList(left, right); + opQuery.put(mongoOperator, termList); + return opQuery; + } + + private Bson getNotQuery(final CQLNotNode node) throws CQLParseException { + Bson left = doParse(node.left); + Bson right = doParse(node.right); + Bson notRight = new BasicDBObject("$not", right); + BasicDBObject andQuery = new BasicDBObject(); + List termList = Lists.newArrayList(left, notRight); + andQuery.put("$and", termList); + return andQuery; + } + + /** + * The construction of the query changes based on the granularity of the date to handle. + *

+ * If the date has yyyy-MM-ddThh:mm:ssZ granularity we have to create a range query because in mongo we have milliseconds, hence an + * exact match will return nothing. + *

+ *

+ * If the date has yyyy-MM-dd granularity then we have to trick the query. If we are interested in the date 2013-10-28, the date has + * been converted into 2013-10-28T00:00:00Z : if we ask for datestamp = 2013-10-28T00:00:00Z, we'll get nothing: we have to ask for + * records whose day is the one specified by the date. + * + *

+ * + * @param indexName + * @param rel + * @param date + * @return + */ + private Bson handleDateRelationNode(final String indexName, final Relation rel, final OAIDate date) { + BasicDBObject mongoQueryObject = new BasicDBObject(); + DateTime fromDate = date.date; + switch (rel) { + case EQUAL: + case EXACT: + if (date.onlyDate) { + DateTime endDate = date.date.plusDays(1); + mongoQueryObject.put(indexName, BasicDBObjectBuilder.start("$gte", fromDate.toDate()).append("$lt", endDate.toDate()).get()); + } else { + DateTime endDate = date.date.plusSeconds(1); + mongoQueryObject.put(indexName, BasicDBObjectBuilder.start("$gte", fromDate.toDate()).append("$lt", endDate.toDate()).get()); + } + break; + case NOT: + mongoQueryObject.put(indexName, new BasicDBObject("$ne", fromDate.toDate())); + break; + case GT: + mongoQueryObject.put(indexName, new BasicDBObject("$gt", fromDate.toDate())); + break; + case GTE: + mongoQueryObject.put(indexName, new BasicDBObject("$gte", fromDate.toDate())); + break; + case LT: + mongoQueryObject.put(indexName, new BasicDBObject("$lt", fromDate.toDate())); + break; + case LTE: + /* + If the request is date <= YYYY-MM-DD then we need to change the date. + The parseDate returned YYYY-MM-DDT00:00:00Z, but we need YYYY-MM-DDT23:59:59Z. + To simplify we can add one day and perform < instead of <=. + */ + if (date.onlyDate) { + fromDate = date.date.plusDays(1); + mongoQueryObject.put(indexName, new BasicDBObject("$lt", fromDate.toDate())); + } else { + mongoQueryObject.put(indexName, new BasicDBObject("$lte", fromDate.toDate())); + } + break; + default: + throw new RuntimeException("Can't parse query: relation " + rel + " not supported!"); + } + return mongoQueryObject; + } + + + private OAIDate parseDate(final String date) { + DateTimeFormatter dateNoTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd").withZoneUTC(); + DateTimeFormatter iso8601NoMsTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZ").withZoneUTC(); + DateTimeFormatter iso8601Formatter = ISODateTimeFormat.dateTime().withZoneUTC(); + OAIDate res = null; + try { + log.debug("Using default " + iso8601Formatter.getClass()); + DateTime dt = iso8601Formatter.parseDateTime(date); + res = new OAIDate(dt, false); + } catch (Exception e) { + try { + log.debug("Switching to ISO with no millisecond date formatter: yyyy-MM-dd'T'HH:mm:ssZ"); + DateTime dt = iso8601NoMsTimeFormatter.parseDateTime(date); + res = new OAIDate(dt, false); + } catch (Exception ex) { + log.debug("Switching to simple date formatter: yyyy-MM-dd"); + DateTime dt = dateNoTimeFormatter.parseDateTime(date); + res = new OAIDate(dt, true); + } + } + return res; + } + + class OAIDate { + + DateTime date; + boolean onlyDate; + + OAIDate(final DateTime date, final boolean onlyDate) { + this.date = date; + this.onlyDate = onlyDate; + } + + } + + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/AndNode.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/AndNode.java new file mode 100644 index 0000000..340d5ea --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/AndNode.java @@ -0,0 +1,14 @@ +package eu.dnetlib.functionality.cql.parse; + +public class AndNode extends BooleanNode { + + public AndNode(Node left, Node right) { + super(left, right); + } + + @Override + public String op() { + return "AND"; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/BooleanNode.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/BooleanNode.java new file mode 100644 index 0000000..bec63c0 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/BooleanNode.java @@ -0,0 +1,33 @@ +package eu.dnetlib.functionality.cql.parse; + +public abstract class BooleanNode extends Node { + + private Node left; + private Node right; + + public BooleanNode(Node left, Node right) { + this.left = left; + this.right = right; + } + + public abstract String op(); + + @Override + public String toString() { + return BooleanNode.class.getSimpleName() + "(" + getLeft() + ", " + getRight() + ")"; + } + + @Override + public String toLucene() { + return "(" + getLeft().toLucene() + " " + op() + " " + getRight().toLucene() + ")"; + } + + public Node getLeft() { + return left; + } + + public Node getRight() { + return right; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/Node.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/Node.java new file mode 100644 index 0000000..a5e0ccf --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/Node.java @@ -0,0 +1,10 @@ +package eu.dnetlib.functionality.cql.parse; + +public abstract class Node { + + @Override + public abstract String toString(); + + public abstract String toLucene(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/NotNode.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/NotNode.java new file mode 100644 index 0000000..e48051b --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/NotNode.java @@ -0,0 +1,20 @@ +package eu.dnetlib.functionality.cql.parse; + +public class NotNode extends BooleanNode { + + public NotNode(Node left, Node right) { + super(left, right); + } + + @Override + public String op() { + return "NOT"; + } + + @Override + public String toLucene() { + return "(" + this.getLeft().toLucene() + " " + op() + " " + this.getRight().toLucene() + ")"; + } + + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/OrNode.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/OrNode.java new file mode 100644 index 0000000..49b910b --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/OrNode.java @@ -0,0 +1,14 @@ +package eu.dnetlib.functionality.cql.parse; + +public class OrNode extends BooleanNode { + + public OrNode(Node left, Node right) { + super(left, right); + } + + @Override + public String op() { + return "OR"; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/Relation.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/Relation.java new file mode 100644 index 0000000..e79fc87 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/Relation.java @@ -0,0 +1,5 @@ +package eu.dnetlib.functionality.cql.parse; + +public enum Relation { + SRC, WITHIN, EQUAL, NOT, GT, GTE, LT, LTE, ANY, ALL, EXACT +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/Relations.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/Relations.java new file mode 100644 index 0000000..b97daca --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/Relations.java @@ -0,0 +1,45 @@ +package eu.dnetlib.functionality.cql.parse; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Maps; + +public final class Relations { + + public static final BiMap MAP = createMap(); + + private Relations() {} + + private static BiMap createMap() { + BiMap map = HashBiMap.create(); + map.forcePut(Relation.SRC, "src"); + map.forcePut(Relation.WITHIN, "within"); + map.forcePut(Relation.EXACT, "exact"); + map.forcePut(Relation.EQUAL, "="); + map.forcePut(Relation.NOT, "<>"); + map.forcePut(Relation.GT, ">"); + map.forcePut(Relation.GTE, ">="); + map.forcePut(Relation.LT, "<"); + map.forcePut(Relation.LTE, "<="); + map.forcePut(Relation.ANY, "any"); + map.forcePut(Relation.ALL, "all"); + return Maps.unmodifiableBiMap(map); + } + + public static String get(Relation rel) { + return MAP.get(rel); + } + + public static Relation get(String rel) { + return MAP.inverse().get(rel); + } + + public static boolean contains(String value) { + return MAP.containsValue(value); + } + + public static boolean contains(Relation rel) { + return MAP.inverse().containsValue(rel); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/TermNode.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/TermNode.java new file mode 100644 index 0000000..70426a3 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/cql/parse/TermNode.java @@ -0,0 +1,179 @@ +package eu.dnetlib.functionality.cql.parse; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import org.apache.commons.lang3.StringUtils; +import org.apache.lucene.queryparser.classic.QueryParserBase; + +public class TermNode extends Node { + + public static final String dnetDefaultField = "__all"; + + private String field; + private Relation rel; + private String value; + private Map> options = Maps.newHashMap(); + private BiMap aliases = HashBiMap.create(); + private Map weights = Maps.newHashMap(); + private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + public TermNode(final String field, final Relation rel, final String value) { + this.field = field; + this.rel = rel; + this.value = value; + simpleDateFormat.setLenient(false); + } + + public TermNode(final String field, final Relation rel, final String value, final Map> options, final BiMap aliases, + final Map weights) { + this(field, rel, value); + this.options = options; + this.aliases = aliases; + this.weights = weights; + } + + @Override + public String toString() { + return TermNode.class.getSimpleName() + "(" + field + " " + rel + " " + value + ")"; + } + + @Override + public String toLucene() { + //StringTokenizer termTokenizer = new StringTokenizer(value, " "); + //StringTokenizer weightTokenizer = new StringTokenizer(value, " "); + final Iterable termTokenizer = Splitter.on(" ").omitEmptyStrings().split(value); + final Iterable weightTokenizer = Splitter.on(" ").omitEmptyStrings().split(value); + switch (rel) { + case EXACT: + final String lucene = getFieldName() + ":" + "\"" + value + "\""; + return StringUtils.isNotBlank(weight()) ? lucene + weight() + " " + expand(value) : lucene; + case EQUAL: + case ALL: + return "(" + handleTokens(termTokenizer, "AND") + " " + expandTokens(weightTokenizer) + ")"; + case ANY: + return "(" + handleTokens(termTokenizer, "OR") + " " + expandTokens(weightTokenizer) + ")"; + case NOT: + return "NOT " + field + ":" + "\"" + value + "\""; + case LT: + if (isDate(value)) { + value = checkDate(value); + } + return field + ":" + "{* TO " + value + "}" + weight(); + case GT: + if (isDate(value)) { + value = checkDate(value); + } + return field + ":" + "{" + value + " TO *}" + weight(); + case LTE: + if (isDate(value)) { + value = checkDate(value); + } + return field + ":" + "[* TO " + value + "]" + weight(); + case GTE: + if (isDate(value)) { + value = checkDate(value); + } + return field + ":" + "[" + value + " TO *]" + weight(); + case WITHIN: + String lowerValue = value.split(" ")[0]; + String upperValue = value.split(" ")[1]; + if (isDate(lowerValue)) { + lowerValue = checkDate(lowerValue); + } + if (isDate(upperValue)) { + upperValue = checkDate(upperValue); + } + return field + ":[" + lowerValue + " TO " + upperValue + "]" + weight(); + default: + throw new RuntimeException("unable to serialize: " + toString()); + } + } + + private String getFieldName() { + return aliases.get(field) != null ? aliases.get(field) : field; + } + + private String weight() { + return (weights != null) && (weights.get(field) != null) ? "^" + weights.get(field) : ""; + } + + private String expandTokens(final Iterable tokens) { + return Joiner.on("").skipNulls().join(Iterables.transform(tokens, new Function() { + @Override + public String apply(final String s) { + if (field.equals(dnetDefaultField.toLowerCase()) || field.equals(dnetDefaultField.toLowerCase())) { + return expand(s); + } + return null; + } + })).trim(); + } + + private String expand(final String token) { + String ret = ""; + if (!weights.keySet().contains(field)) { + for (Entry e : weights.entrySet()) { + ret += e.getKey() + ":\"" + checkEscaping(token) + "\"^" + e.getValue() + " "; + } + } + return ret.trim(); + } + + private String handleTokens(final Iterable tokens, final String op) { + final String separator = " " + op + " "; + return Joiner.on(separator).join(Iterables.transform(tokens, new Function() { + @Override + public String apply(final String s) { + return field + ":" + checkEscaping(s) + weight(); + } + })).trim(); + } + + private String checkEscaping(String token) { + boolean isWildcard = token.contains("*") || token.contains("?"); + boolean isWildcardEnabled = ((options.get("wildcard") != null) && options.get("wildcard").contains("true")) || token.equals("*"); + + if (!(isWildcard & isWildcardEnabled)) { + token = QueryParserBase.escape(token); + } + return token; + } + + private boolean isDate(final String aPossibleDate) { + try { + simpleDateFormat.parse(aPossibleDate); + } catch (ParseException pe) { + return false; + } + return true; + } + + private String checkDate(final String date) { + if (!date.endsWith("Z")) return date + "T00:00:00Z"; + return date; + } + + public String getField() { + return field; + } + + public Relation getRel() { + return rel; + } + + public String getValue() { + return value; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/AbstractIndexClientFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/AbstractIndexClientFactory.java new file mode 100644 index 0000000..bd9a63f --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/AbstractIndexClientFactory.java @@ -0,0 +1,43 @@ +package eu.dnetlib.functionality.index.client; + +import org.springframework.beans.factory.annotation.Autowired; + +import eu.dnetlib.functionality.index.utils.ServiceTools; + +/** + * The Class AbstractIndexClientFactory. + */ +public abstract class AbstractIndexClientFactory implements IndexClientFactory { + + /** The backend id. */ + private String backendId; + + @Autowired + protected ServiceTools isQueryTools; + + /** + * Inits the class. + */ + public abstract void init() throws IndexClientException; + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.client.IndexClientFactory#getBackendId() + */ + @Override + public String getBackendId() { + return backendId; + } + + /** + * Sets the backend id. + * + * @param backendId + * the backend id + */ + public void setBackendId(final String backendId) { + this.backendId = backendId; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/IndexClient.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/IndexClient.java new file mode 100644 index 0000000..ffd8b8f --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/IndexClient.java @@ -0,0 +1,29 @@ +package eu.dnetlib.functionality.index.client; + +import java.io.Closeable; +import java.util.List; +import java.util.Map; + +import eu.dnetlib.functionality.cql.CqlValueTransformerMap; +import eu.dnetlib.functionality.index.client.response.BrowseEntry; +import eu.dnetlib.functionality.index.client.response.LookupResponse; +import eu.dnetlib.functionality.index.query.IndexQueryFactory; +import eu.dnetlib.functionality.index.utils.MetadataReference; + +public interface IndexClient extends Closeable { + + LookupResponse lookup(String query, List filterQuery, int from, int to) throws IndexClientException; + + List browse(String query, List browseFields, int max) throws IndexClientException; + + List browse(String query, List browseFields, int max, List filterQuery) throws IndexClientException; + + long delete(String query) throws IndexClientException; + + CqlValueTransformerMap getCqlValueTransformerMap(final MetadataReference mdRef) throws IndexClientException; + + Map getServiceProperties(); + + IndexQueryFactory getIndexQueryFactory(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/IndexClientException.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/IndexClientException.java new file mode 100644 index 0000000..b8d49b3 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/IndexClientException.java @@ -0,0 +1,28 @@ +package eu.dnetlib.functionality.index.client; + +import eu.dnetlib.data.provision.index.rmi.IndexServiceException; + +public class IndexClientException extends IndexServiceException { + + /** + * + */ + private static final long serialVersionUID = 1851955470714206331L; + + public IndexClientException() { + super(); + } + + public IndexClientException(final String message) { + super(message); + } + + public IndexClientException(final Throwable cause) { + super(cause); + } + + public IndexClientException(final String message, final Throwable cause) { + super(message, cause); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/IndexClientFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/IndexClientFactory.java new file mode 100644 index 0000000..326e752 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/IndexClientFactory.java @@ -0,0 +1,15 @@ +package eu.dnetlib.functionality.index.client; + +import eu.dnetlib.functionality.index.utils.MetadataReference; + +public interface IndexClientFactory { + + String getBackendId(); + + IndexClient getClient(String format, String layout, String interpretation) throws IndexClientException; + + IndexClient getClient(MetadataReference mdRef) throws IndexClientException; + + IndexClient getClient(String collection) throws IndexClientException; + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/response/BrowseEntry.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/response/BrowseEntry.java new file mode 100644 index 0000000..407de93 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/response/BrowseEntry.java @@ -0,0 +1,42 @@ +package eu.dnetlib.functionality.index.client.response; + +import java.util.List; + +public class BrowseEntry { + private String field; + private String label; + private List values; + + public BrowseEntry() {} + + public BrowseEntry(final String field, final String label,final List values) { + this.field = field; + this.label = label; + this.values = values; + } + + public String getField() { + return field; + } + + public void setField(final String field) { + this.field = field; + } + + public String getLabel() { + return label; + } + + public void setLabel(final String label) { + this.label = label; + } + + public List getValues() { + return values; + } + + public void setValues(final List values) { + this.values = values; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/response/BrowseValueEntry.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/response/BrowseValueEntry.java new file mode 100644 index 0000000..69c29dd --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/response/BrowseValueEntry.java @@ -0,0 +1,40 @@ +package eu.dnetlib.functionality.index.client.response; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class BrowseValueEntry implements Comparable { + + private String value; + + private int size; + + public BrowseValueEntry() {} + + public BrowseValueEntry(final String value, final int size) { + this.value = value; + this.size = size; + } + + public String getValue() { + return value; + } + + public void setValue(final String value) { + this.value = value; + } + + public int getSize() { + return size; + } + + public void setSize(final int size) { + this.size = size; + } + + @Override + public int compareTo(final BrowseValueEntry e) { + return Integer.compare(getSize(), e.getSize()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/response/LookupResponse.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/response/LookupResponse.java new file mode 100644 index 0000000..a9bf5f8 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/response/LookupResponse.java @@ -0,0 +1,66 @@ +package eu.dnetlib.functionality.index.client.response; + +import java.util.List; + +import javax.xml.bind.annotation.XmlRootElement; + +import eu.dnetlib.functionality.index.query.QueryResponseParser; + +@XmlRootElement +public class LookupResponse { + + private long total; + private long from; + private long to; + private List records; + + public LookupResponse() {} + + public LookupResponse(final QueryResponseParser parser) { + this.total = parser.getNumFound(); + this.from = parser.getStart(); + this.to = parser.getStart() + parser.getResults().size(); + this.records = parser.getResults(); + + } + + public LookupResponse(final long total, final long from, final long to, final List records) { + this.total = total; + this.from = from; + this.to = to; + this.records = records; + } + + public long getTotal() { + return total; + } + + public void setTotal(final long total) { + this.total = total; + } + + public long getFrom() { + return from; + } + + public void setFrom(final long from) { + this.from = from; + } + + public long getTo() { + return to; + } + + public void setTo(final long to) { + this.to = to; + } + + public List getRecords() { + return records; + } + + public void setRecords(final List records) { + this.records = records; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/solr/SolrIndexClient.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/solr/SolrIndexClient.java new file mode 100644 index 0000000..7e1678c --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/solr/SolrIndexClient.java @@ -0,0 +1,597 @@ +package eu.dnetlib.functionality.index.client.solr; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.*; + +import com.google.common.collect.BiMap; +import com.google.common.collect.Maps; +import eu.dnetlib.data.provision.index.rmi.BrowsingRow; +import eu.dnetlib.data.provision.index.rmi.GroupResult; +import eu.dnetlib.data.provision.index.rmi.IndexServiceException; +import eu.dnetlib.functionality.cql.CqlValueTransformerMap; +import eu.dnetlib.functionality.index.client.IndexClient; +import eu.dnetlib.functionality.index.client.IndexClientException; +import eu.dnetlib.functionality.index.client.response.BrowseEntry; +import eu.dnetlib.functionality.index.client.response.BrowseValueEntry; +import eu.dnetlib.functionality.index.client.response.LookupResponse; +import eu.dnetlib.functionality.index.model.Any.ValueType; +import eu.dnetlib.functionality.index.query.*; +import eu.dnetlib.functionality.index.solr.cql.SolrTypeBasedCqlValueTransformerMapFactory; +import eu.dnetlib.functionality.index.solr.feed.StreamingInputDocumentFactory; +import eu.dnetlib.functionality.index.utils.MetadataReference; +import eu.dnetlib.functionality.index.utils.ZkServers; +import eu.dnetlib.miscutils.datetime.HumanTime; +import eu.dnetlib.miscutils.functional.UnaryFunction; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.impl.CloudSolrClient; +import org.apache.solr.client.solrj.request.LukeRequest; +import org.apache.solr.client.solrj.response.LukeResponse; +import org.apache.solr.client.solrj.response.LukeResponse.FieldInfo; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.client.solrj.response.UpdateResponse; +import org.apache.solr.common.SolrInputDocument; + +/** + * The Class SolrIndexClient. + */ +public class SolrIndexClient implements IndexClient { + + private static final Log log = LogFactory.getLog(SolrIndexClient.class); + + private static final String INDEX_RECORD_RESULT_FIELD = "dnetResult"; + + private static String ZK_ADDRESS = "address"; + + /** The format. */ + private String format; + + /** The layout. */ + private String layout; + + /** The interpretation. */ + private String interpretation; + + protected Map serviceProperties; + + /** The client. */ + private CloudSolrClient client; + + private SolrIndexQueryFactory solrIndexQueryFactory; + + /** The query response factory. */ + private QueryResponseFactory queryResponseFactory; + + private SolrTypeBasedCqlValueTransformerMapFactory tMapFactory; + + /** + * The Constructor. + * + * @param format + * the format + * @param layout + * the layout + * @param interpretation + * the interpretation + * @param serviceProperties + * the service properties + * @param tMapFactory + */ + public SolrIndexClient(final String format, final String layout, final String interpretation, final Map serviceProperties, + final SolrIndexQueryFactory indexQueryFactory, final QueryResponseFactory queryResponseFactory, + final SolrTypeBasedCqlValueTransformerMapFactory tMapFactory) { + this.format = format; + this.layout = layout; + this.interpretation = interpretation; + this.serviceProperties = serviceProperties; + this.solrIndexQueryFactory = indexQueryFactory; + this.queryResponseFactory = queryResponseFactory; + this.tMapFactory = tMapFactory; + + log.debug(String.format("Created a new instance of the index of type %s-%s-%s", format, layout, interpretation)); + } + + /** + * Do delete. + * + * @param query + * the CQL query + * @return true, if do delete + * @throws IndexServiceException + * the index service exception + */ + @Override + public long delete(final String query) throws IndexClientException { + try { + log.debug("delete by query: " + query); + MetadataReference mdRef = new MetadataReference(getFormat(), getLayout(), getInterpretation()); + SolrIndexQuery translatedQuery = (SolrIndexQuery) solrIndexQueryFactory.getIndexQuery(QueryLanguage.CQL, query, this, mdRef); + String tquery = translatedQuery.getQuery(); + translatedQuery.setQueryLimit(0); + + SolrIndexQueryResponse rsp = new SolrIndexQueryResponse(getClient().query(translatedQuery)); + QueryResponseParser responseParser = queryResponseFactory.getQueryResponseParser(rsp, mdRef); + long total = responseParser.getNumFound(); + getClient().deleteByQuery(tquery); + getClient().commit(); + return total; + } catch (Exception e) { + throw new IndexClientException("unable to run delete by query: " + query, e); + } + } + + /** + * {@inheritDoc} + * + * @throws IndexClientException + * + * @see IndexClient#browse(String, List, int) + */ + @Override + public List browse(final String query, final List browseFields, final int max) throws IndexClientException { + MetadataReference mdRef = new MetadataReference(getFormat(), getLayout(), getInterpretation()); + SolrIndexQuery translatedQuery = buildBrowseQuery(query, browseFields, max, mdRef); + return executeBrowseQuery(query, translatedQuery, mdRef, browseFields); + } + + /** + * {@inheritDoc} + * + * @throws IndexClientException + * + * @see IndexClient#browse(String, List, int, List) + */ + @Override + public List browse(final String query, final List browseFields, final int max, final List filterQuery) + throws IndexClientException { + MetadataReference mdRef = new MetadataReference(getFormat(), getLayout(), getInterpretation()); + SolrIndexQuery translatedQuery = buildBrowseQuery(query, browseFields, max, mdRef); + if (filterQuery != null) { + log.debug("Filter Query:"); + for (String fq : filterQuery) { + translatedQuery.addFilterQuery(fq); + log.debug("- " + fq); + } + } + return executeBrowseQuery(query, translatedQuery, mdRef, browseFields); + + } + + private SolrIndexQuery buildBrowseQuery(final String query, final List browseFields, final int max, final MetadataReference mdRef) + throws IndexClientException { + log.debug("Browse request for the index collection for query:" + query); + + SolrIndexQuery translatedQuery = (SolrIndexQuery) solrIndexQueryFactory.getIndexQuery(QueryLanguage.CQL, query, this, mdRef); + translatedQuery.setFacet(true); + if (browseFields != null) { + List browsableFields = solrIndexQueryFactory.getBrowsableFields(browseFields, mdRef); + log.debug("Browsing fields:"); + for (String field : browsableFields) { + translatedQuery.addFacetField(field); + log.debug("- " + field); + + } + translatedQuery.setFacetLimit(max); + log.debug("max number of browsing field :" + max); + } + return translatedQuery; + } + + private List executeBrowseQuery(final String originalQuery, + final SolrIndexQuery query, + final MetadataReference mdRef, + final List browseFields) throws IndexClientException { + try { + SolrIndexQueryResponse response = new SolrIndexQueryResponse(getClient().query(query)); + QueryResponseParser responseParser = queryResponseFactory.getQueryResponseParser(response, mdRef); + List results = responseParser.getBrowsingResults(); + List out = convertBrowseEntry(browseFields, results, responseParser.getAliases()); + return out; + } catch (SolrServerException | IOException e) { + throw new IndexClientException("Error on executing a query " + originalQuery, e); + } + } + + /** + * Creates the connection. + * + * @return the string + * @throws IndexClientException + * the index client exception + */ + private String getUrl() throws IndexClientException { + String address = serviceProperties.get(ZK_ADDRESS); + if (StringUtils.isBlank(address)) { + throw new IndexClientException("Unable to load a solr client, missing zk address"); + } + return address; + } + + /** + * Gets the client. + * + * @return the client + * @throws IndexClientException + * the index client exception + */ + public SolrClient getClient() throws IndexClientException { + if (this.client == null) { + String url = getUrl(); + log.debug("create new Client " + url); + + final ZkServers zk = ZkServers.newInstance(url); + client = new CloudSolrClient.Builder(zk.getHosts(), zk.getChroot()).build(); + + client.connect(); + client.setDefaultCollection(String.format("%s-%s-%s", getFormat(), getLayout(), getInterpretation())); + try { + client.ping(); + } catch (Exception e) { + throw new IndexClientException("oops something went wrong", e); + } + } + return client; + } + + /** + * Sets the client. + * + * @param client + * the client + */ + public void setClient(final CloudSolrClient client) { + this.client = client; + } + + @Override + public LookupResponse lookup(final String query, final List filterQuery, final int from, final int to) throws IndexClientException { + log.debug("lookup request for the index collection for query:" + query); + MetadataReference mdRef = new MetadataReference(getFormat(), getLayout(), getInterpretation()); + SolrIndexQuery translatedQuery = (SolrIndexQuery) solrIndexQueryFactory.getIndexQuery(QueryLanguage.CQL, query, this, mdRef); + translatedQuery.setQueryOffset(from); + translatedQuery.setQueryLimit(to - from + 1); + if (filterQuery != null) { + for (String fq : filterQuery) { + translatedQuery.addFilterQuery(fq); + } + } + + try { + SolrIndexQueryResponse response = new SolrIndexQueryResponse(getClient().query(translatedQuery)); + QueryResponseParser responseParser = queryResponseFactory.getQueryResponseParser(response, mdRef); + + return new LookupResponse(responseParser); + } catch (SolrServerException | IOException e) { + throw new IndexClientException("Error on executing a query " + query, e); + } + + } + + @Override + public CqlValueTransformerMap getCqlValueTransformerMap(final MetadataReference mdRef) throws IndexClientException { + try { + return tMapFactory.getIt(readFieldNamesAndTypes()); + } catch (Exception e) { + throw new IndexClientException(e); + } + } + + @Override + public IndexQueryFactory getIndexQueryFactory() { + return solrIndexQueryFactory; + } + + @Override + public void close() throws IOException { + log.debug("shutdown client: " + serviceProperties.get(ZK_ADDRESS)); + client.close(); + } + + public int feed(final String record, final String indexDsId, final UnaryFunction toIndexRecord) throws IndexClientException { + return feed(record, indexDsId, toIndexRecord, true); + } + + public int feed(final String record, final String indexDsId, final UnaryFunction toIndexRecord, final boolean commit) + throws IndexClientException { + try { + final SolrInputDocument doc = prepareSolrDocument(record, indexDsId, toIndexRecord); + if ((doc == null) || doc.isEmpty()) throw new IndexClientException("Invalid solr document"); + return feed(doc, commit); + } catch (final Throwable e) { + throw new IndexClientException("Error feeding document", e); + } + } + + public int feed(final SolrInputDocument document) throws IndexClientException { + return feed(document, true); + } + + public int feed(final List document) throws IndexClientException { + try { + final UpdateResponse res = getClient().add(document); + log.debug("feed time for single records, elapsed time: " + HumanTime.exactly(res.getElapsedTime())); + if (res.getStatus() != 0) { throw new IndexClientException("bad status: " + res.getStatus()); } + return res.getStatus(); + } catch (final Throwable e) { + throw new IndexClientException("Error feeding document", e); + } + } + + public int feed(final SolrInputDocument document, final boolean commit) throws IndexClientException { + try { + final UpdateResponse res = getClient().add(document); + log.debug("feed time for single records, elapsed time: " + HumanTime.exactly(res.getElapsedTime())); + if (res.getStatus() != 0) { throw new IndexClientException("bad status: " + res.getStatus()); } + if (commit) { + getClient().commit(); + } + return res.getStatus(); + } catch (final Throwable e) { + throw new IndexClientException("Error feeding document", e); + } + } + + public void feed(final List docs, final AfterFeedingCallback callback) throws IndexClientException { + feed(docs, callback, true); + } + + public void feed(final List docs, final AfterFeedingCallback callback, final boolean commit) throws IndexClientException { + try { + if (docs.isEmpty()) { + log.debug("Empty list of documents. Calling callback, if needed."); + if (callback != null) { + callback.doAfterFeeding(null); + } + return; + } + final UpdateResponse res = getClient().add(docs); + + log.debug("feed time for " + docs.size() + " records, elapsed tipe: : " + HumanTime.exactly(res.getElapsedTime())); + + if (commit) { + getClient().commit(); + } + if (callback != null) { + callback.doAfterFeeding(res); + } + if (res.getStatus() != 0) throw new IndexClientException("bad status: " + res.getStatus()); + } catch (final Throwable e) { + throw new IndexClientException("Error feeding documents", e); + } + } + + public SolrInputDocument prepareSolrDocument(final String record, final String indexDsId, final UnaryFunction toIndexRecord) + throws IndexClientException { + try { + final StreamingInputDocumentFactory documentFactory = new StreamingInputDocumentFactory(); + + final String version = (new SimpleDateFormat("yyyy-MM-dd\'T\'hh:mm:ss\'Z\'")).format(new Date()); + final String indexRecord = toIndexRecord.evaluate(record); + + if (log.isDebugEnabled()) { + log.debug("***************************************\nSubmitting index record:\n" + indexRecord + "\n***************************************\n"); + } + + return documentFactory.parseDocument(version, indexRecord, indexDsId, INDEX_RECORD_RESULT_FIELD); + } catch (final Throwable e) { + throw new IndexClientException("Error creating solr document", e); + } + } + + public boolean isRecordIndexed(final String id) throws IndexClientException { + final QueryResponse res = query("objidentifier:\"" + id + "\"", null); + return res.getResults().size() > 0; + } + + public int remove(final String id) throws IndexClientException { + return remove(id, true); + } + + public UpdateResponse commit() throws IndexClientException { + try { + return getClient().commit(); + } catch (SolrServerException | IOException e) { + throw new IndexClientException(e); + } + } + + public int remove(final String id, final boolean commit) throws IndexClientException { + try { + final UpdateResponse res = getClient().deleteByQuery("objidentifier:\"" + id + "\""); + if (commit) { + getClient().commit(); + } + return res.getResponse().size(); + } catch (final Throwable e) { + throw new IndexClientException("Error removing documents", e); + } + } + + public int count(final String query) throws IndexClientException { + final QueryResponse res = query(query, 0); + return res.getResults().size(); + } + + public QueryResponse query(final String query, Integer rows) throws IndexClientException { + try { + final SolrQuery solrQuery = new SolrQuery(); + solrQuery.setQuery(query); + if(rows != null && rows >= 0) { + solrQuery.setRows(rows); + } + return getClient().query(solrQuery); + } catch (final Throwable e) { + throw new IndexClientException("Error searching documents", e); + } + } + + public UpdateResponse deleteByQuery(final String query) throws IndexClientException { + try { + return getClient().deleteByQuery(query); + } catch (final Throwable e) { + throw new IndexClientException("Error searching documents", e); + } + } + + public interface AfterFeedingCallback { + + void doAfterFeeding(final UpdateResponse response); + } + + /** + * Gets the format. + * + * @return the format + */ + public String getFormat() { + return format; + } + + /** + * Sets the format. + * + * @param format + * the format + */ + public void setFormat(final String format) { + this.format = format; + } + + /** + * Gets the layout. + * + * @return the layout + */ + public String getLayout() { + return layout; + } + + /** + * Sets the layout. + * + * @param layout + * the layout + */ + public void setLayout(final String layout) { + this.layout = layout; + } + + /** + * Gets the interpretation. + * + * @return the interpretation + */ + public String getInterpretation() { + return interpretation; + } + + /** + * Sets the interpretation. + * + * @param interpretation + * the interpretation + */ + public void setInterpretation(final String interpretation) { + this.interpretation = interpretation; + } + + public Map getServiceProperties() { + return serviceProperties; + } + + public void setServiceProperties(final Map serviceProperties) { + this.serviceProperties = serviceProperties; + } + + // HELPERS + + private List convertBrowseEntry(final List browseFields, final List results, final BiMap aliases) { + + Map mapResult = new HashMap(); + for (BrowsingRow row : results) { + for (GroupResult groupResult : row.getGroupResult()) { + String name = groupResult.getName(); + List valuesEntry; + BrowseEntry entry; + if (mapResult.containsKey(name)) { + entry = mapResult.get(name); + valuesEntry = entry.getValues(); + if (valuesEntry == null) { + valuesEntry = new ArrayList(); + entry.setValues(valuesEntry); + } + + } else { + entry = new BrowseEntry(); + entry.setField(name); + entry.setLabel(name); + valuesEntry = new ArrayList(); + entry.setValues(valuesEntry); + mapResult.put(name, entry); + } + String value = groupResult.getValue(); + int count = groupResult.getCount(); + BrowseValueEntry entryValue = new BrowseValueEntry(value, count); + valuesEntry.add(entryValue); + } + } + List out = new ArrayList(); + for (String b : browseFields) { + String inverse = null; + if (aliases != null) { + inverse = aliases.get(b) != null ? aliases.get(b) : aliases.inverse().get(b); + } + if (mapResult.containsKey(b)) { + out.add(mapResult.get(b)); + } else if (mapResult.containsKey(inverse) == true) { + BrowseEntry data = mapResult.get(inverse); + data.setField(b); + out.add(data); + } + } + return out; + } + + private Map readFieldNamesAndTypes() throws SolrServerException, IOException, IndexClientException { + + final LukeRequest request = new LukeRequest(); + request.setShowSchema(true); + + request.setNumTerms(0); + final LukeResponse response = request.process(getClient()); + final Map fieldInfos = response.getFieldInfo(); + final Map fieldTypeInfos = response.getFieldTypeInfo(); + final Map result = Maps.newHashMap(); + for (FieldInfo fieldInfo : fieldInfos.values()) { + LukeResponse.FieldTypeInfo fieldTypeInfo = fieldTypeInfos.get(fieldInfo.getType()); + final String fieldName = fieldTypeInfo.getName().toLowerCase(); + final ValueType fieldType = resolveSolrTypeClassName(fieldName); + result.put(fieldInfo.getName(), fieldType); + } + return result; + } + + private ValueType resolveSolrTypeClassName(final String solrTypeName) { + if (solrTypeName.contains("LongField")) { + return ValueType.LONG; + } else if (solrTypeName.contains("IntField")) { + return ValueType.LONG; + } else if (solrTypeName.contains("short")) { + return ValueType.LONG; + } else if (solrTypeName.contains("float")) { + return ValueType.DOUBLE; + } else if (solrTypeName.contains("double")) { + return ValueType.DOUBLE; + } else if (solrTypeName.contains("date")) { + return ValueType.DATETIME; + } else { + return ValueType.STRING; + } + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/solr/SolrIndexClientFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/solr/SolrIndexClientFactory.java new file mode 100644 index 0000000..76aec61 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/client/solr/SolrIndexClientFactory.java @@ -0,0 +1,121 @@ +package eu.dnetlib.functionality.index.client.solr; + +import java.lang.reflect.Type; +import java.util.Map; + +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import eu.dnetlib.functionality.index.client.AbstractIndexClientFactory; +import eu.dnetlib.functionality.index.client.IndexClient; +import eu.dnetlib.functionality.index.client.IndexClientException; +import eu.dnetlib.functionality.index.client.IndexClientFactory; +import eu.dnetlib.functionality.index.query.SolrIndexQueryFactory; +import eu.dnetlib.functionality.index.query.SolrIndexQueryResponseFactory; +import eu.dnetlib.functionality.index.solr.cql.SolrTypeBasedCqlValueTransformerMapFactory; +import eu.dnetlib.functionality.index.utils.MetadataReference; +import eu.dnetlib.functionality.index.utils.MetadataReferenceFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Required; + +/** + * The Class SolrIndexClientFactory. + */ +public class SolrIndexClientFactory extends AbstractIndexClientFactory implements IndexClientFactory { + + /** The id. */ + private static String ID = "id"; + + /** The configuration. */ + private String configuration; + + /** The type token. */ + protected Type typeToken = new TypeToken>() {}.getType(); + + /** The service properties. */ + private Map serviceProperties; + + @Autowired + private SolrIndexQueryFactory solrIndexQueryFactory; + + @Autowired + private SolrIndexQueryResponseFactory solrIndexQueryResponseFactory; + + @Autowired + private SolrTypeBasedCqlValueTransformerMapFactory tMapFactory; + + /** + * Inits the class. + * + * @throws IndexClientException + * the index client exception + */ + @Override + public void init() throws IndexClientException { + try { + serviceProperties = new Gson().fromJson(getConfiguration(), typeToken); + } catch (Throwable e) { + throw new IndexClientException("unable to parse configuration: " + getConfiguration(), e); + } + } + + /** + * {@inheritDoc} + * + * @see IndexClientFactory#getBackendId() + */ + @Override + public String getBackendId() { + return serviceProperties.get(SolrIndexClientFactory.ID); + } + + /** + * {@inheritDoc} + * + * @throws IndexClientException + * + * @see IndexClientFactory#getClient(String, String, String) + */ + @Override + public IndexClient getClient(final String format, final String layout, final String interpretation) throws IndexClientException { + return new SolrIndexClient(format, layout, interpretation, isQueryTools.getIndexProperties(getBackendId()), solrIndexQueryFactory, + solrIndexQueryResponseFactory, tMapFactory); + } + + /** + * {@inheritDoc} + * + * @throws IndexClientException + * + * @see IndexClientFactory#getClient(MetadataReference) + */ + @Override + public IndexClient getClient(final MetadataReference mdRef) throws IndexClientException { + return getClient(mdRef.getFormat(), mdRef.getLayout(), mdRef.getInterpretation()); + } + + @Override + public IndexClient getClient(final String collection) throws IndexClientException { + return getClient(MetadataReferenceFactory.decodeMetadata(collection)); + } + + /** + * Gets the configuration. + * + * @return the configuration + */ + public String getConfiguration() { + return configuration; + } + + /** + * Sets the configuration. + * + * @param configuration + * the configuration + */ + @Required + public void setConfiguration(final String configuration) { + this.configuration = configuration; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/Any.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/Any.java new file mode 100755 index 0000000..879fa71 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/Any.java @@ -0,0 +1,115 @@ +package eu.dnetlib.functionality.index.model; + +import java.util.Iterator; + +/** + * Interface for metadata objects. + */ +public interface Any extends Iterable { + + /** + * Enumeration with the possible value types. + * + */ + public enum ValueType { + + /** + * The list with the possible value types. MAP map, SEQ sequence, STRING string value, DOUBLE double value, + * BOOLEAN Boolean value, LONG long value, DATE date (without time), DATETIME date with time + */ + MAP, SEQ, STRING, DOUBLE, BOOLEAN, LONG, DATE, DATETIME + }; + + /** + * @return value type + */ + ValueType getValueType(); + + /** + * @return true if value is of type map + */ + boolean isMap(); + + /** + * @return true if value is of type sequence + */ + boolean isSeq(); + + /** + * @return true if value is of type value + */ + boolean isValue(); + + /** + * @return true if value is of type string + */ + boolean isString(); + + /** + * @return true if value is of type double + */ + boolean isDouble(); + + /** + * @return true if value is of type boolean + */ + boolean isBoolean(); + + /** + * @return true if value is of type long + */ + boolean isLong(); + + /** + * @return true if value is of type date + */ + boolean isDate(); + + /** + * @return true if value is of type date time + */ + boolean isDateTime(); + + /** + * @return true if value is of type long or double + */ + boolean isNumber(); + + /** + * @return data factory to create Anys of the same kind. + */ + DataFactory getFactory(); + + /** + * @return iterator over elements. For {@link Value}, this return an iterator about the value itself. For + * {@link AnyMap} it returns an iterator on the values. + */ + @Override + Iterator iterator(); + + /** + * @return true, if the Any does not have content. For {@link Value}: returns always false. + */ + boolean isEmpty(); + + /** + * @return size of Any. For {@link Value} this is always 1. + */ + int size(); + + /** + * returns this Any as an {@link Value} object or throws an {@link InvalidValueTypeException}. + */ + Value asValue(); + + /** + * returns this Any as an {@link AnyMap} object or throws an {@link InvalidValueTypeException}. + */ + AnyMap asMap(); + + /** + * returns this Any as an {@link AnySeq} object or throws an {@link InvalidValueTypeException}. + */ + AnySeq asSeq(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/AnyMap.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/AnyMap.java new file mode 100755 index 0000000..8d6464a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/AnyMap.java @@ -0,0 +1,159 @@ +package eu.dnetlib.functionality.index.model; + +import java.util.Date; +import java.util.Map; + +/** + * Interface for a map with Any objects. + * + *

+ * AnyMap does not allow null values! + *

+ * + */ +public interface AnyMap extends Any, Map { + + /** + * Adds the given value to the mapped entry of the given key. If there's no mapping yet, the new value is added as + * Seq with one value. If the key is currently mapped to a Value/Map, the Value/Map is converted to a Seq containing + * the Value/Map and the Seq is added. If the key is currently mapped to a Seq, new value is added to that Seq. + * + * @param key + * The key + * @param value + * the Any to add + */ + void add(String key, Any value); + + /** + * @param key + * The key + * @param value + * String value, must not be null. + * @return the previous value or null + */ + Any put(String key, String value); + + /** + * Long, Integer, Short and Byte values will be converted to Value object of type LONG, all others to Value object + * of type DOUBLE. + * + * @param key + * The key + * @param value + * Number value, will be converted, must not be null. + * @return the previous value or null + */ + Any put(String key, Number value); + + /** + * @param key + * The key + * @param value + * Boolean value, must not be null. + * @return the previous value or null + */ + Any put(String key, Boolean value); + + /** + * @param key + * The key + * @param value + * Any value, must not be null. + * @return the previous value or null + */ + @Override + Any put(String key, Any value); + + /** + * @param key + * The key + * @return The AnyMap matching the key, an InvalidValueTypeException is thrown if the value is not of type AnyMap, + * or null + */ + AnyMap getMap(String key); + + /** + * Gets the map and optionally creates one under the given key if not present. + * + * @param key + * The key + * @param create + * if true and the key is not yet used, create a new map. else just return null. + * @return The AnyMap matching the key, an InvalidValueTypeException is thrown if the value is not of type AnyMap + */ + AnyMap getMap(String key, boolean create); + + /** + * @param key + * The key + * @return The AnySeq matching to this key, an InvalidValueTypeException is thrown if the value is not of type, or + * null + */ + AnySeq getSeq(String key); + + /** + * Gets the seq and optionally creates one under the given key if not present. + * + * @param key + * The key + * @param create + * the create + * @return The AnySeq matching the key, an InvalidValueTypeException is thrown if the value is not of type AnyMap + */ + AnySeq getSeq(String key, boolean create); + + /** + * @param key + * the key + * @return The string representation of the attribute value + */ + String getStringValue(String key); + + /** + * @param key + * the key + * @return The value matching to this key, an InvalidValueTypeException is thrown if the value is no value type + */ + Value getValue(String key); + + /** + * @param key + * The key + * @return The double value matching to this key, an InvalidValueTypeException is thrown if the value is not of type + * double + */ + Double getDoubleValue(String key); + + /** + * @param key + * The key + * @return The long value matching to this key, an InvalidValueTypeException is thrown if the value is not of type + * long + */ + Long getLongValue(String key); + + /** + * @param key + * The key + * @return The boolean value matching to this key, an InvalidValueTypeException is thrown if the value is not of + * type boolean + */ + Boolean getBooleanValue(String key); + + /** + * @param key + * The key + * @return The date value matching to this key, an InvalidValueTypeException is thrown if the value is not of type + * date + */ + Date getDateValue(String key); + + /** + * @param key + * The key + * @return The date time value matching to this key, an InvalidValueTypeException is thrown if the value is not of + * type date time + */ + Date getDateTimeValue(String key); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/AnySeq.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/AnySeq.java new file mode 100755 index 0000000..f5c060c --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/AnySeq.java @@ -0,0 +1,128 @@ +package eu.dnetlib.functionality.index.model; + +import java.util.Date; +import java.util.List; + +/** + * Interface for a sequence of Any objects. + * + *

+ * AnySeq does not allow null values! + *

+ * + */ +public interface AnySeq extends List, Any { + + /** + * @param element + * The string object to add + * @return true if successfully added, false else + */ + boolean add(String element); + + /** + * Long, Integer, Short and Byte values will be converted to Value object of type LONG, all others to Value object + * of type DOUBLE. + * + * @param number + * The number object to add + * @return true if successfully added, false else + */ + boolean add(Number number); + + /** + * @param index + * The index where to add the any object + * @param element + * The any object to add + */ + @Override + void add(int index, Any element); + + /** + * @param index + * The index of the object to return + * @return The AnyMap matching to the index, an InvalidValueTypeException is thrown if the value is not of type + * AnyMap + */ + AnyMap getMap(int index); + + /** + * @param index + * The index of the object to return + * @return The AnySeq matching to this index, an InvalidValueTypeException is thrown if the value is not of type + */ + AnySeq getSeq(int index); + + /** + * @param index + * The index of the object to return + * @return The value matching to this index, an InvalidValueTypeException is thrown if the value is no value type. + */ + Value getValue(int index); + + /** + * @param index + * The index of the object to return + * @return The string value matching to this index, an InvalidValueTypeException is thrown if the value is not of + * type string + */ + String getStringValue(int index); + + /** + * @param index + * The index of the object to return + * @return The double value matching to this index, an InvalidValueTypeException is thrown if the value is not of + * type double + */ + Double getDoubleValue(int index); + + /** + * @param index + * The index of the object to return + * @return The long value matching to this index, an InvalidValueTypeException is thrown if the value is not of type + * long + */ + Long getLongValue(int index); + + /** + * @param index + * The index of the object to return + * @return The boolean value matching to this index, an InvalidValueTypeException is thrown if the value is not of + * type boolean + */ + Boolean getBooleanValue(int index); + + /** + * @param index + * The index of the object to return + * @return The date value matching to this index, an InvalidValueTypeException is thrown if the value is not of type + * date + */ + Date getDateValue(int index); + + /** + * @param index + * The index of the object to return + * @return The date time value matching to this index, an InvalidValueTypeException is thrown if the value is not of + * type date time + */ + Date getDateTimeValue(int index); + + /** + * returns all values as a List of Strings. + * + * @throws InvalidValueTypeException + * if not all contained values are strings. + */ + List asStrings(); + + /** + * returns all values as a List of Long. + * + * @throws InvalidValueTypeException + * if not all contained values are Longs. + */ + List asLongs(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/DataFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/DataFactory.java new file mode 100644 index 0000000..fe192f8 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/DataFactory.java @@ -0,0 +1,157 @@ +package eu.dnetlib.functionality.index.model; + +import java.util.Date; + +import eu.dnetlib.functionality.index.model.Any.ValueType; + +/** + * Interface for creation of data objects. + */ +public interface DataFactory { + + /** default instance. */ + DataFactory DEFAULT = DataFactoryCreator.newInstance(); + + /** + * @return The created AnyMap object + */ + AnyMap createAnyMap(); + + /** + * @return The created AnySeq object + */ + AnySeq createAnySeq(); + + /** + * @param value + * the String to create the Value from. + * @return The created Value object. + */ + Value createStringValue(String value); + + /** + * @param value + * the Boolean to create the Value from. + * @return The created Value object. + */ + Value createBooleanValue(Boolean value); + + /** + * @param value + * the Long to create the Value from. + * @return The created Value object. + */ + Value createLongValue(Long value); + + /** + * @param value + * the int to create the Value from. + * @return The created Value object. + */ + Value createLongValue(int value); + + /** + * @param value + * the Double to create the Value from. + * @return The created Value object. + */ + Value createDoubleValue(Double value); + + /** + * @param value + * the float to create the Value from. + * @return The created Value object. + */ + Value createDoubleValue(float value); + + /** + * @param value + * the Date to create the Value from. + * @return The created Value object. + */ + Value createDateValue(Date value); + + /** + * @param value + * the DateTime to create the Value from. + * @return The created Value object. + */ + Value createDateTimeValue(Date value); + + /** + * @param value + * The value + * @param type + * The type + * @return The Value object with correct type, InvalidvalueTypeException else. + */ + Value parseFromString(String value, String type); + + /** + * @param value + * The value + * @param valueType + * The value's type + * @return The Value object with correct type, InvalidvalueTypeException else. + */ + Value parseFromString(final String value, final ValueType valueType); + + /** + * Tries to convert the String to a Date or Timestamp Value, if not possible return a String Value. + * + * @param value + * The value to check for Date/Timestamp + * @return The Value object with guessed type, InvalidvalueTypeException if value is null. + */ + Value tryDateTimestampParsingFromString(String value); + + /** + * @param object + * The object + * @return The value matching the class of given object, InvalidValueTypeException otherwise. + * @deprecated Use {@link #autoConvertValue(Object)} instead + */ + @Deprecated + Value parseFromObject(final Object object); + + /** + * auto converts the given object into the object's corresponding Value. + * + * @param object + * The object, must be one of the simple types + * @return The value matching the class of given object, InvalidValueTypeException otherwise. + * + */ + Value autoConvertValue(final Object object); + + /** + * Clone Any object. + * + * @param source + * the source + * + * @return the attribute + */ + Any cloneAny(final Any source); + + /** + * Clone AnyMap object. + * + * @param source + * the source + * + * @return the attribute + */ + AnyMap cloneAnyMap(final AnyMap source); + + /** + * Clone AnySeq object. + * + * @param source + * the source + * + * @return the attribute + */ + AnySeq cloneAnySeq(final AnySeq source); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/DataFactoryCreator.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/DataFactoryCreator.java new file mode 100644 index 0000000..d559cc0 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/DataFactoryCreator.java @@ -0,0 +1,22 @@ +package eu.dnetlib.functionality.index.model; + +import eu.dnetlib.functionality.index.model.impl.DefaultDataFactoryImpl; + +/** + * Helper class to decouple Any and Record interfaces better from default implementation. + */ +public final class DataFactoryCreator { + + /** + * Private default constructor to avoid instance creation. + */ + private DataFactoryCreator() { + } + + /** + * @return instance of the default DataFactory. + */ + public static DataFactory newInstance() { + return new DefaultDataFactoryImpl(); + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/InvalidValueTypeException.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/InvalidValueTypeException.java new file mode 100755 index 0000000..fdfd7d7 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/InvalidValueTypeException.java @@ -0,0 +1,66 @@ +package eu.dnetlib.functionality.index.model; + +/** + * A runtime exception that will be thrown if the value types of the Any objects do not match the expected types. + * + */ +public class InvalidValueTypeException extends RuntimeException { + + /** exceptions can be serialized. */ + private static final long serialVersionUID = 1L; + + /** unsupported value type that caused this exception. */ + private final Class _unsupportedType; + + /** + * Creates an InvalidValueTypeException with the given message. + * + * @param message + * The message for the user. + */ + public InvalidValueTypeException(final String message) { + this(message, (Class) null); + } + + /** + * Instantiates a new invalid value type exception. + * + * @param message + * the message + * @param unsupportedType + * the unsupported type + */ + public InvalidValueTypeException(final String message, final Class unsupportedType) { + super(message); + _unsupportedType = unsupportedType; + } + + /** + * Instantiates a new invalid value type exception. + * + * @param unsupportedType + * the unsupported type + */ + public InvalidValueTypeException(final Class unsupportedType) { + this("type not supported: " + unsupportedType, unsupportedType); + } + + /** + * @param format + * @param e + */ + public InvalidValueTypeException(final String message, final Exception e) { + super(message, e); + _unsupportedType = null; + } + + /** + * Gets the unsupported type. might be null. + * + * @return the unsupported type + */ + public Class getUnsupportedType() { + return _unsupportedType; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/Value.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/Value.java new file mode 100755 index 0000000..701b980 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/Value.java @@ -0,0 +1,49 @@ +package eu.dnetlib.functionality.index.model; + +import java.util.Date; + +/** + * Interface for a value object, used if the any object is of type string, double, boolean, long, date or date time. + */ +public interface Value extends Any { + + /** + * @return The string representation + */ + String asString(); + + /** + * @return The double representation, an InvalidValueTypeException is thrown if the value is not a number. If value + * is a string then this is tried to be converted to a Double. + */ + Double asDouble(); + + /** + * @return The long representation, an InvalidValueTypeException is thrown if the value is not a number. If value is + * a string then this is tried to be converted to a Long. + */ + Long asLong(); + + /** + * @return The boolean representation, an InvalidValueTypeException is thrown if the value is not of type boolean. + * If value is a string then this is tried to be converted to a boolean. + */ + Boolean asBoolean(); + + /** + * @return The date representation, an InvalidValueTypeException is thrown if the value is not of type date or + * datetime. If value is a string then this is tried to be converted to a date. + */ + Date asDate(); + + /** + * @return The date time representation, an InvalidValueTypeException is thrown if the value is not of type + * datetime. If value is a string then this is tried to be converted to a datetime. + */ + Date asDateTime(); + + /** + * @return the value object + */ + Object getObject(); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/ValueFormatHelper.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/ValueFormatHelper.java new file mode 100644 index 0000000..9343663 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/ValueFormatHelper.java @@ -0,0 +1,227 @@ +package eu.dnetlib.functionality.index.model; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import java.util.regex.Pattern; + +/** + * helper class for formatting and parsing Values. all methods synchronize on the used local formatter object, so you + * can use the shared instance. Using multiple instances may improve performance, though, because of less + * synchronization. + */ +public class ValueFormatHelper { + /** shared global helper instance. */ + public static final ValueFormatHelper INSTANCE = new ValueFormatHelper(); + + /** The max. length of strings to be parsed as date. */ + private static final int DATE_LENGTH = 10; + + /** The length of strings to be parsed as date time for (default) pattern 1. */ + private static final int DATE_TIME_LENGTH_PATTERN_DEFAULT = 28; + + /** The length of strings to be parsed as date time for pattern 2 and 3. */ + private static final int DATE_TIME_LENGTH_PATTERN_2_AND_3 = 24; + + /** The length of strings to be parsed as date time for pattern 4. */ + private static final int DATE_TIME_LENGTH_PATTERN_4 = 20; + + /** formatter to create and parse standard string representations of Date values: "yyyy-MM-dd". */ + private final DateFormat _formatDate = new SimpleDateFormat("yyyy-MM-dd"); + + /** valid Datetime value pattern with milliseconds and time zone (default for printing). */ + private final DateFormat _formatDateTimePatternDefault = getDefaultDateTimeFormat(); + + /** valid Datetime value pattern with time zone. */ + private final DateFormat _formatDateTimePattern2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + + /** valid Datetime value pattern with milliseconds and time zone 'Z' (UTC). */ + private final DateFormat _formatDateTimePattern3 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + + /** valid Datetime value pattern without milliseconds and time zone 'Z' (UTC). */ + private final DateFormat _formatDateTimePattern4 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + + /** pattern for checking if the string might be a date. */ + private final Pattern _formatPatternDateDefault = Pattern.compile("\\d{4}-\\d{2}-\\d{2}"); + + /** pattern for checking if the string might be a date time for default pattern. */ + private final Pattern _formatPatternTimeDefault = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}[+-]{1}\\d{4}"); + + /** pattern for checking if the string might be a date time for pattern 2. */ + private final Pattern _formatPatternTime2 = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]{1}\\d{4}"); + + /** pattern for checking if the string might be a date time for pattern 3. */ + private final Pattern _formatPatternTime3 = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z"); + + /** pattern for checking if the string might be a date time for pattern 4. */ + private final Pattern _formatPatternTime4 = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z"); + + /** + * create local instance. + */ + public ValueFormatHelper() { + _formatDate.setLenient(false); + _formatDateTimePatternDefault.setLenient(false); + _formatDateTimePattern2.setLenient(false); + _formatDateTimePattern3.setLenient(false); + _formatDateTimePattern3.setTimeZone(TimeZone.getTimeZone("UTC")); + _formatDateTimePattern4.setLenient(false); + _formatDateTimePattern4.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + /** + * @return the default format for datetime values. + */ + public static SimpleDateFormat getDefaultDateTimeFormat() { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + sdf.setLenient(false); + return sdf; + } + + /** + * format value as Date string. + * + * @param value + * a date value + * @return formatted date. + */ + public String formatDate(final Date value) { + synchronized (_formatDate) { + return _formatDate.format(value); + } + } + + /** + * format value as DateTime string. + * + * @param value + * a datetime value + * @return formatted datetime string + */ + public String formatDateTime(final Date value) { + synchronized (_formatDateTimePatternDefault) { + return _formatDateTimePatternDefault.format(value); + } + } + + /** + * parse a date string. + * + * @param dateString + * a date string + * @return parsed Date + * @throws ParseException + * string has wrong format + */ + public Date parseDate(final String dateString) throws ParseException { + if (dateString.length() == DATE_LENGTH && _formatPatternDateDefault.matcher(dateString).matches()) { + synchronized (_formatDate) { + return _formatDate.parse(dateString); + } + } else { + throw new ParseException("Length of date string '" + dateString + "' exceeds maximum date length of " + DATE_LENGTH, DATE_LENGTH); + } + } + + /** + * parse a date string. + * + * @param dateString + * a date string + * @return parsed Date if correct format, else null. + */ + public Date tryParseDate(final String dateString) { + if (dateString.length() == DATE_LENGTH && _formatPatternDateDefault.matcher(dateString).matches()) { + synchronized (_formatDate) { + try { + return _formatDate.parse(dateString); + } catch (final ParseException ex) { + ; // not a date ... fine. + } + } + } + return null; + } + + /** + * parse datetime string. + * + * @param dateTimeString + * a datetime string + * @return parsed Date + * @throws ParseException + * string has wrong format + */ + public Date parseDateTime(final String dateTimeString) throws ParseException { + final Date result; + final int dateLen = dateTimeString.length(); + if (dateLen == DATE_TIME_LENGTH_PATTERN_DEFAULT && _formatPatternTimeDefault.matcher(dateTimeString).matches()) { + synchronized (_formatDateTimePatternDefault) { + result = _formatDateTimePatternDefault.parse(dateTimeString); + } + } else if (dateLen == DATE_TIME_LENGTH_PATTERN_2_AND_3 && _formatPatternTime2.matcher(dateTimeString).matches()) { + synchronized (_formatDateTimePattern2) { + result = _formatDateTimePattern2.parse(dateTimeString); + } + } else if (dateLen == DATE_TIME_LENGTH_PATTERN_2_AND_3 && _formatPatternTime3.matcher(dateTimeString).matches()) { + synchronized (_formatDateTimePattern3) { + result = _formatDateTimePattern3.parse(dateTimeString); + } + } else if (dateLen == DATE_TIME_LENGTH_PATTERN_4 && _formatPatternTime4.matcher(dateTimeString).matches()) { + synchronized (_formatDateTimePattern4) { + result = _formatDateTimePattern4.parse(dateTimeString); + } + } else { + throw new ParseException("Length '" + dateTimeString.length() + "' of datetime string '" + dateTimeString + + "' doesn't match expected pattern length", dateTimeString.length()); + } + return result; + } + + /** + * parse datetime string. + * + * @param dateTimeString + * a datetime string + * @return parsed Date if correct format, else null; + */ + public Date tryParseDateTime(final String dateTimeString) { + Date result = null; + try { + switch (dateTimeString.length()) { + case DATE_TIME_LENGTH_PATTERN_DEFAULT: + if (_formatPatternTimeDefault.matcher(dateTimeString).matches()) { + synchronized (_formatDateTimePatternDefault) { + result = _formatDateTimePatternDefault.parse(dateTimeString); + } + } + break; + case DATE_TIME_LENGTH_PATTERN_2_AND_3: + if (_formatPatternTime2.matcher(dateTimeString).matches()) { + synchronized (_formatDateTimePattern2) { + result = _formatDateTimePattern2.parse(dateTimeString); + } + } else if (_formatPatternTime3.matcher(dateTimeString).matches()) { + synchronized (_formatDateTimePattern3) { + result = _formatDateTimePattern3.parse(dateTimeString); + } + } + break; + case DATE_TIME_LENGTH_PATTERN_4: + if (_formatPatternTime4.matcher(dateTimeString).matches()) { + synchronized (_formatDateTimePattern4) { + result = _formatDateTimePattern4.parse(dateTimeString); + } + } + break; + default: + break; + } + } catch (final ParseException ex) { + ; // not a datetime ... fine. + } + return result; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/AbstractIndexDocument.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/AbstractIndexDocument.java new file mode 100644 index 0000000..f7e946d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/AbstractIndexDocument.java @@ -0,0 +1,393 @@ +package eu.dnetlib.functionality.index.model.document; + +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import eu.dnetlib.functionality.index.model.Any.ValueType; +import eu.dnetlib.functionality.index.utils.IndexFieldUtility; + +/** + * The Class AbstractDocument Represent the field information needed to construct and index Document. + */ +public abstract class AbstractIndexDocument implements Map, Iterable, IndexDocument { + + /** The _fields. */ + protected final Map fields; + + private static final String DATE_SUFFIX = "\\+\\d{2}:\\d{2}"; + + /** + * Actual document status. + */ + private Status status; + + /** + * index schema. + */ + protected final Map schema; + + /** + * If there was an error building the document, it is described here. + */ + private Throwable error; + + /** + * Instantiates a new abstract document. + */ + public AbstractIndexDocument(final Map schema, final String dsId) { + this.schema = schema; + fields = new LinkedHashMap(); + // addField(IndexFieldUtility.DS_ID, dsId); + } + + /** + * Instantiates a new abstract document. + * + * @param fields + * the fields + */ + public AbstractIndexDocument(final Map schema, final String dsId, final Map fields) { + this.schema = schema; + this.fields = fields; + addField(IndexFieldUtility.DS_ID, dsId); + } + + /** + * Remove all fields and boosts from the document. + */ + @Override + public void clear() { + if (fields != null) { + fields.clear(); + } + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#addField(java.lang.String, java.lang.Object) + */ + @Override + public void addField(final String name, final Object value) { + Object hack_value = hackField(name, value); + IndexField field = fields.get(name); + if ((field == null) || (field.getValue() == null)) { + setField(name, hack_value); + } else { + field.addValue(hack_value); + } + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#getFieldValue(java.lang.String) + */ + @Override + public Object getFieldValue(final String name) { + IndexField field = getField(name); + Object o = null; + if (field != null) { + o = field.getFirstValue(); + } + return o; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#getField(java.lang.String) + */ + @Override + public IndexField getField(final String field) { + return fields.get(field); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#getFieldValues(java.lang.String) + */ + @Override + public Collection getFieldValues(final String name) { + IndexField field = getField(name); + if (field != null) return field.getValues(); + return null; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#getFieldNames() + */ + @Override + public Collection getFieldNames() { + return fields.keySet(); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#setField(java.lang.String, java.lang.Object) + */ + @Override + public void setField(final String name, final Object value) { + Object hack_value = hackField(name, value); + IndexField field = new IndexField(name); + fields.put(name, field); + field.setValue(hack_value); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#removeField(java.lang.String) + */ + @Override + public IndexField removeField(final String name) { + return fields.remove(name); + } + + /** + * {@inheritDoc} + * + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator iterator() { + return fields.values().iterator(); + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "IndexDocument: " + fields.values(); + } + + // --------------------------------------------------- + // MAP interface + // --------------------------------------------------- + + /** + * {@inheritDoc} + * + * @see java.util.Map#containsKey(java.lang.Object) + */ + @Override + public boolean containsKey(final Object key) { + return fields.containsKey(key); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#containsValue(java.lang.Object) + */ + @Override + public boolean containsValue(final Object value) { + return fields.containsValue(value); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#entrySet() + */ + @Override + public Set> entrySet() { + return fields.entrySet(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#get(java.lang.Object) + */ + @Override + public IndexField get(final Object key) { + return fields.get(key); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#isEmpty() + */ + @Override + public boolean isEmpty() { + return fields.isEmpty(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#keySet() + */ + @Override + public Set keySet() { + return fields.keySet(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#put(java.lang.Object, java.lang.Object) + */ + @Override + public IndexField put(final String key, final IndexField value) { + return fields.put(key, value); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#putAll(java.util.Map) + */ + @Override + public void putAll(final Map t) { + fields.putAll(t); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#remove(java.lang.Object) + */ + @Override + public IndexField remove(final Object key) { + return fields.remove(key); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#size() + */ + @Override + public int size() { + return fields.size(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#values() + */ + @Override + public Collection values() { + return fields.values(); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#getStatus() + */ + @Override + public Status getStatus() { + return status; + } + + /** + * Sets the status. + * + * @param status + * the status to set + * @return the index document + */ + @Override + public IndexDocument setStatus(final Status status) { + this.status = status; + return this; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#deepCopy() + */ + @Override + public IndexDocument deepCopy() { + // TODO Auto-generated method stub + return null; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#getError() + */ + @Override + public Throwable getError() { + + return error; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#setOK() + */ + @Override + public IndexDocument setOK() { + + return this.setStatus(Status.OK); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#setMarked() + */ + @Override + public IndexDocument setMarked() { + addField(IndexFieldUtility.DELETE_DOCUMENT, true); + return this.setStatus(Status.MARKED); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.functionality.index.model.document.IndexDocument#setError(java.lang.Throwable) + */ + @Override + public IndexDocument setError(final Throwable error) { + this.error = error; + return this.setStatus(Status.ERROR); + } + + /** + * This method modifies a field w.r.t. his type + * + * @param name + * @param value + * @return + */ + private Object hackField(final String name, Object value) { + if (schema == null) return value; + + final ValueType type = schema.get(name); + + // TODO: hack this is hardcoded for the date type + switch (type) { + case DATETIME: + case DATE: + if (value.toString().matches(DATE_SUFFIX)) return value.toString().replaceAll(DATE_SUFFIX, "Z"); + if (value.toString().endsWith("Z")) return value; + + value = value + "T00:00:00Z"; + break; + default: + break; + } + return value; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/IndexDocument.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/IndexDocument.java new file mode 100644 index 0000000..f24a3d1 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/IndexDocument.java @@ -0,0 +1,130 @@ +package eu.dnetlib.functionality.index.model.document; + +import java.util.Collection; + +/** + * The Interface IndexDocument. + */ +public interface IndexDocument { + + /** + * Adds a field with the given name, value and boost. If a field with the name already exists, then the given value is appended to the + * value of that field, with the new boost. If the value is a collection, then each of its values will be added to the field. + * + * The class type of value and the name parameter should match schema.xml. schema.xml can be found in conf directory under the solr home + * by default. + * + * @param name + * Name of the field, should match one of the field names defined under "fields" tag in schema.xml. + * @param value + * Value of the field, should be of same class type as defined by "type" attribute of the corresponding field in schema.xml. + */ + public void addField(String name, Object value); + + /** + * Set a field with implied null value for boost. + * + * @param name + * name of the field to set + * @param value + * value of the field + */ + public void setField(String name, Object value); + + /** + * Get the first value for a field. + * + * @param name + * name of the field to fetch + * @return first value of the field or null if not present + */ + public Object getFieldValue(String name); + + /** + * Gets the field. + * + * @param field + * the field + * @return the field + */ + public IndexField getField(String field); + + /** + * Remove a field from the document. + * + * @param name + * The field name whose field is to be removed from the document + * @return the previous field with name, or null if there was no field for key. + */ + public IndexField removeField(String name); + + /** + * Get all the values for a field. + * + * @param name + * name of the field to fetch + * @return value of the field or null if not set + */ + public Collection getFieldValues(String name); + + /** + * Get all field names. + * + * @return Set of all field names. + */ + public Collection getFieldNames(); + + /** + * return a copy of index Document. + * + * @return the index document + */ + public IndexDocument deepCopy(); + + /** + * Gets the status. + * + * @return the status + */ + public Status getStatus(); + + /** + * The set status. + * + * @param status + * the status + * @return the index document + */ + public IndexDocument setStatus(Status status); + + /** + * If there was an error building the document, it is described here. + * + * @return the error + */ + public Throwable getError(); + + /** + * Sets the error. + * + * @param error + * the error + * @return the index document + */ + public IndexDocument setError(final Throwable error); + + /** + * Sets the ok status to the index document. + * + * @return the index document + */ + public IndexDocument setOK(); + + /** + * Sets the status marked to the index document. + * + * @return the index document + */ + public IndexDocument setMarked(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/IndexField.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/IndexField.java new file mode 100644 index 0000000..f8d1e2b --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/IndexField.java @@ -0,0 +1,218 @@ +package eu.dnetlib.functionality.index.model.document; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +/** + * The Class IndexField defines a common representation of field that each back-end of index should contain. + */ +public class IndexField implements Iterable { + + /** The name. */ + private String name; + + /** The value. */ + private Object value = null; + + public IndexField(final String n) { + this.name = n; + } + + /** + * Set the value for a field. Arrays will be converted to a collection. If a collection is given, then that collection will be used as + * the backing collection for the values. + * + * @param v + * the v + */ + public void setValue(final Object v) { + + if (v instanceof Object[]) { + Object[] arr = (Object[]) v; + Collection c = new ArrayList(arr.length); + for (Object o : arr) { + c.add(o); + } + value = c; + } else { + value = v; + } + } + + /** + * Add values to a field. If the added value is a collection, each value will be added individually. + * + * @param v + * the v + */ + @SuppressWarnings("unchecked") + public void addValue(final Object v) { + if (value == null) { + if (v instanceof Collection) { + Collection c = new ArrayList(3); + for (Object o : (Collection) v) { + c.add(o); + } + setValue(c); + } else { + setValue(v); + } + + return; + } + Collection vals = null; + if (value instanceof Collection) { + vals = (Collection) value; + } else { + vals = new ArrayList(3); + vals.add(value); + value = vals; + } + + // Add the new values to a collection + if (v instanceof Iterable) { + for (Object o : (Iterable) v) { + vals.add(o); + } + } else if (v instanceof Object[]) { + for (Object o : (Object[]) v) { + vals.add(o); + } + } else { + vals.add(v); + } + } + + /** + * Gets the first value. + * + * @return the first value + */ + @SuppressWarnings("unchecked") + public Object getFirstValue() { + if (value instanceof Collection) { + @SuppressWarnings("rawtypes") + Collection c = (Collection) value; + if (c.size() > 0) return c.iterator().next(); + return null; + } + return value; + } + + /** + * Gets the value. + * + * @return the value for this field. If the field has multiple values, this will be a collection. + */ + public Object getValue() { + return value; + } + + /** + * Gets the values. + * + * @return the values for this field. This will return a collection even if the field is not multi-valued + */ + @SuppressWarnings("unchecked") + public Collection getValues() { + if (value instanceof Collection) return (Collection) value; + if (value != null) { + Collection vals = new ArrayList(1); + vals.add(value); + return vals; + } + return null; + } + + /** + * Gets the value count. + * + * @return the number of values for this field + */ + @SuppressWarnings("rawtypes") + public int getValueCount() { + if (value instanceof Collection) return ((Collection) value).size(); + return (value == null) ? 0 : 1; + } + + // --------------------------------------------------------------- + // --------------------------------------------------------------- + + /** + * Gets the name. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Sets the name. + * + * @param name + * the new name + */ + public void setName(final String name) { + this.name = name; + } + + /** + * {@inheritDoc} + * + * @see java.lang.Iterable#iterator() + */ + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Iterator iterator() { + if (value instanceof Collection) return ((Collection) value).iterator(); + return new Iterator() { + + boolean nxt = (value != null); + + @Override + public boolean hasNext() { + return nxt; + } + + @Override + public Object next() { + nxt = false; + return value; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return name + value; + } + + public IndexField deepCopy() { + IndexField clone = new IndexField(name); + + // We can't clone here, so we rely on simple primitives + if (value instanceof Collection) { + @SuppressWarnings("unchecked") + Collection values = (Collection) value; + Collection cloneValues = new ArrayList(values.size()); + cloneValues.addAll(values); + clone.value = cloneValues; + } else { + clone.value = value; + } + return clone; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/Status.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/Status.java new file mode 100644 index 0000000..a296928 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/document/Status.java @@ -0,0 +1,14 @@ +package eu.dnetlib.functionality.index.model.document; + +/** + * Possible IndexDocument Status. + */ +public enum Status { + + /** The marked. */ + MARKED, + /** The error. */ + ERROR, + /** The ok. */ + OK +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/AbstractAny.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/AbstractAny.java new file mode 100755 index 0000000..5e556aa --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/AbstractAny.java @@ -0,0 +1,121 @@ +package eu.dnetlib.functionality.index.model.impl; + +import java.io.Serializable; + +import eu.dnetlib.functionality.index.model.*; + +import static java.lang.String.format; + +/** + * Basic abstract implementation class for Any. + */ +public abstract class AbstractAny implements Any, Serializable { + + /** because it is serializable. */ + private static final long serialVersionUID = 1L; + + /** + * type of this value object. + */ + protected final ValueType _valueType; + + /** + * Constructs a new AbstractAny. + * + * @param valueType + * the type of the AnbstractAny. + */ + protected AbstractAny(final ValueType valueType) { + _valueType = valueType; + } + + /** {@inheritDoc} */ + @Override + public ValueType getValueType() { + return _valueType; + } + + /** {@inheritDoc} */ + @Override + public boolean isMap() { + return _valueType == ValueType.MAP; + } + + /** {@inheritDoc} */ + @Override + public boolean isSeq() { + return _valueType == ValueType.SEQ; + } + + /** {@inheritDoc} */ + @Override + public boolean isValue() { + // return _valueType == ValueType.STRING || _valueType == ValueType.DOUBLE || _valueType == ValueType.BOOLEAN + // || _valueType == ValueType.LONG || _valueType == ValueType.DATE || _valueType == ValueType.DATETIME; + return _valueType != ValueType.MAP && _valueType != ValueType.SEQ; + } + + /** {@inheritDoc} */ + @Override + public boolean isString() { + return _valueType == ValueType.STRING; + } + + /** {@inheritDoc} */ + @Override + public boolean isDouble() { + return _valueType == ValueType.DOUBLE; + } + + /** {@inheritDoc} */ + @Override + public boolean isBoolean() { + return _valueType == ValueType.BOOLEAN; + } + + /** {@inheritDoc} */ + @Override + public boolean isLong() { + return _valueType == ValueType.LONG; + } + + /** {@inheritDoc} */ + @Override + public boolean isDate() { + return _valueType == ValueType.DATE; + } + + /** {@inheritDoc} */ + @Override + public boolean isDateTime() { + return _valueType == ValueType.DATETIME; + } + + /** {@inheritDoc} */ + @Override + public boolean isNumber() { + return _valueType == ValueType.LONG || _valueType == ValueType.DOUBLE; + } + + /** {@inheritDoc} */ + @Override + public DataFactory getFactory() { + return DefaultDataFactoryImpl.INSTANCE; + } + + @Override + public Value asValue() { + throw new InvalidValueTypeException(format("cannot return a %s as a Value", _valueType)); + } + + @Override + public AnyMap asMap() { + throw new InvalidValueTypeException(format("cannot return a %s as a %s", _valueType, ValueType.MAP)); + } + + @Override + public AnySeq asSeq() { + throw new InvalidValueTypeException(format("cannot return a %s as a %s", _valueType, ValueType.SEQ)); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/AnyMapImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/AnyMapImpl.java new file mode 100755 index 0000000..2864694 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/AnyMapImpl.java @@ -0,0 +1,361 @@ +package eu.dnetlib.functionality.index.model.impl; + +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import eu.dnetlib.functionality.index.model.Any; +import eu.dnetlib.functionality.index.model.AnyMap; +import eu.dnetlib.functionality.index.model.AnySeq; +import eu.dnetlib.functionality.index.model.InvalidValueTypeException; +import eu.dnetlib.functionality.index.model.Value; + +/** + * Class implementing AnyMap. + */ +public final class AnyMapImpl extends AbstractAny implements AnyMap { + + /** version. */ + private static final long serialVersionUID = 1L; + + /** internal representation of the AnyMap. */ + private final Map _anyMap; + + /** Constructs a new AnyMapImpl. */ + AnyMapImpl() { + super(ValueType.MAP); + _anyMap = new LinkedHashMap(); + } + + /** {@inheritDoc} */ + @Override + public void add(final String key, final Any value) { + final Any any = _anyMap.get(key); + AnySeq anySeq = null; + if (any == null) { + anySeq = getFactory().createAnySeq(); + _anyMap.put(key, anySeq); + } else if (any.isValue() || any.isMap()) { + anySeq = getFactory().createAnySeq(); + anySeq.add(any); + _anyMap.put(key, anySeq); + } else { // any.isSeq() + anySeq = (AnySeq) any; + } + anySeq.add(value); + } + + /** {@inheritDoc} */ + @Override + public void clear() { + _anyMap.clear(); + } + + /** {@inheritDoc} */ + @Override + public boolean isEmpty() { + return _anyMap.isEmpty(); + } + + /** {@inheritDoc} */ + @Override + public Set keySet() { + return _anyMap.keySet(); + } + + /** {@inheritDoc} */ + @Override + public Any put(final String key, final String value) { + try { + return put(key, new ValueImpl(ValueType.STRING, value)); + } catch (final Exception e) { + throw new IllegalArgumentException("cannot add value for key: " + key, e); + } + } + + /** {@inheritDoc} */ + @Override + public Any put(final String key, final Number value) { + if (value == null) { + throw new IllegalArgumentException("The value of any Any must not be null."); + } + if (value instanceof Double) { + return put(key, new ValueImpl(ValueType.DOUBLE, value)); + } else if (value instanceof Long) { + return put(key, new ValueImpl(ValueType.LONG, value)); + } else if (value instanceof Integer) { + return put(key, new ValueImpl(ValueType.LONG, Long.valueOf(value.longValue()))); + } else if (value instanceof Short) { + return put(key, new ValueImpl(ValueType.LONG, Long.valueOf(value.longValue()))); + } else if (value instanceof Byte) { + return put(key, new ValueImpl(ValueType.LONG, Long.valueOf(value.longValue()))); + } else { // default: DOUBLE + return put(key, new ValueImpl(ValueType.DOUBLE, Double.valueOf(value.doubleValue()))); + } + } + + /** {@inheritDoc} */ + @Override + public Any put(final String key, final Boolean value) { + return put(key, new ValueImpl(ValueType.BOOLEAN, value)); + } + + /** {@inheritDoc} */ + @Override + public Any put(final String key, final Any value) { + if (value == null) { + throw new IllegalArgumentException("The value of any Any must not be null."); + } + return _anyMap.put(key, value); + } + + /** {@inheritDoc} */ + @Override + public int size() { + return _anyMap.size(); + } + + /** {@inheritDoc} */ + @Override + public Collection values() { + return _anyMap.values(); + } + + /** {@inheritDoc} */ + @Override + public Iterator iterator() { + return _anyMap.values().iterator(); + } + + /** {@inheritDoc} */ + @Override + public AnyMap getMap(final String key) { + final Any anyValue = get(key); + if (anyValue != null) { + if (anyValue.isMap()) { + return (AnyMap) anyValue; + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to AnyMap."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public AnySeq getSeq(final String key) { + final Any anyValue = get(key); + if (anyValue != null) { + if (anyValue.isSeq()) { + return (AnySeq) anyValue; + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to AnySeq."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Value getValue(final String key) { + final Any anyValue = get(key); + if (anyValue != null) { + if (anyValue instanceof Value) { + return (Value) anyValue; + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to Value."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public String getStringValue(final String key) { + final Any anyValue = get(key); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asString(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to String."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Double getDoubleValue(final String key) { + final Any anyValue = get(key); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asDouble(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to double."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Long getLongValue(final String key) { + final Any anyValue = get(key); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asLong(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to long."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Boolean getBooleanValue(final String key) { + final Any anyValue = get(key); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asBoolean(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to boolean."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Date getDateValue(final String key) { + final Any anyValue = get(key); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asDate(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to Date."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Date getDateTimeValue(final String key) { + final Any anyValue = get(key); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asDateTime(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to DateTime."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (_anyMap == null ? 0 : _anyMap.hashCode()); + return result; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final AnyMapImpl other = (AnyMapImpl) obj; + if (_anyMap == null) { + if (other._anyMap != null) { + return false; + } + } else if (!_anyMap.equals(other._anyMap)) { + return false; + } + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return _anyMap.toString(); + } + + /** {@inheritDoc} */ + @Override + public boolean containsKey(final Object key) { + return _anyMap.containsKey(key); + } + + /** {@inheritDoc} */ + @Override + public boolean containsValue(final Object value) { + return _anyMap.containsValue(value); + } + + /** {@inheritDoc} */ + @Override + public Any get(final Object key) { + return _anyMap.get(key); + } + + /** {@inheritDoc} */ + @Override + public void putAll(final Map map) { + _anyMap.putAll(map); + } + + /** {@inheritDoc} */ + @Override + public Any remove(final Object key) { + return _anyMap.remove(key); + } + + /** {@inheritDoc} */ + @Override + public Set> entrySet() { + return _anyMap.entrySet(); + } + + @Override + public AnyMap asMap() { + return this; + } + + @Override + public AnyMap getMap(final String key, final boolean create) { + AnyMap val = getMap(key); + if (val == null && create) { + val = getFactory().createAnyMap(); + this.put(key, val); + } + return val; + + } + + @Override + public AnySeq getSeq(final String key, final boolean create) { + AnySeq val = getSeq(key); + if (val == null && create) { + val = getFactory().createAnySeq(); + this.put(key, val); + } + return val; + + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/AnySeqImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/AnySeqImpl.java new file mode 100755 index 0000000..26f77c8 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/AnySeqImpl.java @@ -0,0 +1,391 @@ +package eu.dnetlib.functionality.index.model.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import eu.dnetlib.functionality.index.model.Any; +import eu.dnetlib.functionality.index.model.AnyMap; +import eu.dnetlib.functionality.index.model.AnySeq; +import eu.dnetlib.functionality.index.model.InvalidValueTypeException; +import eu.dnetlib.functionality.index.model.Value; + +/** + * Sequence of Any objects. + */ +public final class AnySeqImpl extends AbstractAny implements AnySeq { + + /** version. */ + private static final long serialVersionUID = 1L; + + /** holds the Any objects of this AnySeq. */ + private final List _anyList; + + /** + * constructs a new instance of AnySeqImpl. + */ + AnySeqImpl() { + super(ValueType.SEQ); + _anyList = new ArrayList(); + } + + /** {@inheritDoc} */ + @Override + public boolean add(final Any value) { + if (value == null) { + throw new IllegalArgumentException("The value of any Any must not be null."); + } + return _anyList.add(value); + } + + /** {@inheritDoc} */ + @Override + public boolean add(final String e) { + return add(new ValueImpl(ValueType.STRING, e)); + } + + /** {@inheritDoc} */ + @Override + public boolean add(final Number n) { + if (n instanceof Double) { + return add(new ValueImpl(ValueType.DOUBLE, n)); + } else if (n instanceof Long) { + return add(new ValueImpl(ValueType.LONG, n)); + } else if (n instanceof Integer || n instanceof Short || n instanceof Byte) { + return add(new ValueImpl(ValueType.LONG, Long.valueOf(n.longValue()))); + } else { // default: DOUBLE + return add(new ValueImpl(ValueType.DOUBLE, Double.valueOf(n.doubleValue()))); + } + } + + /** {@inheritDoc} */ + @Override + public void add(final int index, final Any element) { + if (element == null) { + throw new IllegalArgumentException("The value of any Any must not be null."); + } + _anyList.add(index, element); + } + + /** {@inheritDoc} */ + @Override + public boolean addAll(final Collection c) { + return _anyList.addAll(c); + } + + /** {@inheritDoc} */ + @Override + public boolean addAll(final int index, final Collection c) { + return _anyList.addAll(index, c); + } + + /** {@inheritDoc} */ + @Override + public void clear() { + _anyList.clear(); + } + + /** {@inheritDoc} */ + @Override + public boolean contains(final Object o) { + return _anyList.contains(o); + } + + /** {@inheritDoc} */ + @Override + public boolean containsAll(final Collection c) { + return _anyList.containsAll(c); + } + + /** {@inheritDoc} */ + @Override + public Any get(final int index) { + return _anyList.get(index); + } + + /** {@inheritDoc} */ + @Override + public Any set(final int index, final Any element) { + if (element == null) { + throw new IllegalArgumentException("The value of any Any must not be null."); + } + return _anyList.set(index, element); + } + + /** {@inheritDoc} */ + @Override + public int indexOf(final Object o) { + return _anyList.indexOf(o); + } + + /** {@inheritDoc} */ + @Override + public boolean isEmpty() { + return _anyList.isEmpty(); + } + + /** {@inheritDoc} */ + @Override + public Iterator iterator() { + return _anyList.iterator(); + } + + /** {@inheritDoc} */ + @Override + public int lastIndexOf(final Object o) { + return _anyList.lastIndexOf(o); + } + + /** {@inheritDoc} */ + @Override + public ListIterator listIterator() { + return _anyList.listIterator(); + } + + /** {@inheritDoc} */ + @Override + public ListIterator listIterator(final int index) { + return _anyList.listIterator(index); + } + + /** {@inheritDoc} */ + @Override + public Any remove(final int index) { + return _anyList.remove(index); + } + + /** {@inheritDoc} */ + @Override + public boolean remove(final Object o) { + return _anyList.remove(o); + } + + /** {@inheritDoc} */ + @Override + public boolean removeAll(final Collection c) { + return _anyList.removeAll(c); + } + + /** {@inheritDoc} */ + @Override + public boolean retainAll(final Collection c) { + return _anyList.retainAll(c); + } + + /** {@inheritDoc} */ + @Override + public int size() { + return _anyList.size(); + } + + /** {@inheritDoc} */ + @Override + public List subList(final int fromIndex, final int toIndex) { + return _anyList.subList(fromIndex, toIndex); + } + + /** {@inheritDoc} */ + @Override + public Object[] toArray() { + return _anyList.toArray(); + } + + /** {@inheritDoc} */ + @Override + public T[] toArray(final T[] a) { + return _anyList.toArray(a); + } + + /** {@inheritDoc} */ + @Override + public AnyMap getMap(final int index) { + final Any anyValue = get(index); + if (anyValue != null) { + if (anyValue.isMap()) { + return (AnyMap) anyValue; + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to AnyMap."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public AnySeq getSeq(final int index) { + final Any anyValue = get(index); + if (anyValue != null) { + if (anyValue.isSeq()) { + return (AnySeq) anyValue; + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to AnySeq."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Value getValue(final int index) { + final Any anyValue = get(index); + if (anyValue != null) { + if (anyValue instanceof Value) { + return (Value) anyValue; + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to Value."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public String getStringValue(final int index) { + final Any anyValue = get(index); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asString(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to String."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Double getDoubleValue(final int index) { + final Any anyValue = get(index); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asDouble(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to double."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Long getLongValue(final int index) { + final Any anyValue = get(index); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asLong(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to long."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Boolean getBooleanValue(final int index) { + final Any anyValue = get(index); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asBoolean(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to boolean."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Date getDateValue(final int index) { + final Any anyValue = get(index); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asDate(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to Date."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public Date getDateTimeValue(final int index) { + final Any anyValue = get(index); + if (anyValue != null) { + if (anyValue instanceof Value) { + return ((Value) anyValue).asDateTime(); + } else { + throw new InvalidValueTypeException("Cannot convert value of type '" + anyValue.getValueType() + "' to DateTime."); + } + } + return null; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (_anyList == null ? 0 : _anyList.hashCode()); + return result; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof AnySeqImpl)) { + return false; + } + final AnySeqImpl other = (AnySeqImpl) obj; + if (_anyList == null) { + if (other._anyList != null) { + return false; + } + } else if (!_anyList.equals(other._anyList)) { + return false; + } + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return _anyList.toString(); + } + + @Override + public AnySeq asSeq() { + return this; + } + + @Override + public List asStrings() { + final List values = new ArrayList(_anyList.size()); + for (final Any any : _anyList) { + values.add(any.asValue().asString()); + } + return values; + } + + @Override + public List asLongs() { + final List values = new ArrayList(_anyList.size()); + for (final Any any : _anyList) { + values.add(any.asValue().asLong()); + } + return values; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/DateValue.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/DateValue.java new file mode 100644 index 0000000..e7bad8f --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/DateValue.java @@ -0,0 +1,78 @@ +package eu.dnetlib.functionality.index.model.impl; + +import java.io.Serializable; +import java.util.Date; + +/** + * Pair of a Date and optionally the original string from which it was parsed. Used to ensure that Date or Timestamp + * Values parsed from JSON or BON string values are reproduced in exactly the same form when converted to a string + * again. + */ +public final class DateValue implements Serializable { + + /** serializable. */ + private static final long serialVersionUID = 1L; + + /** the actual date or timestamp value. */ + private final Date _date; + + /** the original string, if the date/timestamp value is parsed from a string and not created immediately in Java. */ + private final String _originalString; + + /** create immediately from a java date. */ + DateValue(final Date date) { + this(date, null); + } + + /** create a pair of a date and the string it was parsed from. */ + public DateValue(final Date date, final String originalString) { + super(); + _date = date; + _originalString = originalString; + } + + /** @return the date. */ + public Date getDate() { + return _date; + } + + /** @return {@link Date#getTime()} of the contained date. */ + public long getTime() { + return _date.getTime(); + } + + /** @return the original string, if the date was parsed. */ + public String getOriginalString() { + return _originalString; + } + + /** @return true if an original string is stored. */ + public boolean hasOriginalString() { + return _originalString != null; + } + + /** + * @return true if the other object is a {@link DateValue}, too, and contains an equals date object. The original + * string is not relevant for the comparison. + */ + @Override + public boolean equals(final Object obj) { + if (obj != null && obj instanceof DateValue) { + return _date.equals(((DateValue) obj)._date); + } + return false; + } + + /** @return hashCode of the contained date object. */ + @Override + public int hashCode() { + return _date.hashCode(); + } + + /** @return toString of contained date object. */ + @Override + public String toString() { + return _date.toString(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/DefaultDataFactoryImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/DefaultDataFactoryImpl.java new file mode 100644 index 0000000..c5e8da2 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/DefaultDataFactoryImpl.java @@ -0,0 +1,243 @@ +package eu.dnetlib.functionality.index.model.impl; + +import static java.lang.String.format; + +import java.util.Date; +import java.util.Iterator; +import java.util.Map.Entry; + +import org.apache.commons.lang3.NotImplementedException; + +import eu.dnetlib.functionality.index.model.Any; +import eu.dnetlib.functionality.index.model.AnyMap; +import eu.dnetlib.functionality.index.model.AnySeq; +import eu.dnetlib.functionality.index.model.DataFactory; +import eu.dnetlib.functionality.index.model.InvalidValueTypeException; +import eu.dnetlib.functionality.index.model.Value; +import eu.dnetlib.functionality.index.model.ValueFormatHelper; +import eu.dnetlib.functionality.index.model.Any.ValueType; + +/** + * Implementation of DataFactory. + * + */ +public class DefaultDataFactoryImpl implements DataFactory { + + /** instance for sharing. */ + public static final DefaultDataFactoryImpl INSTANCE = new DefaultDataFactoryImpl(); + + /** immutable empyty map. */ + public static final AnyMap IMMUTABLE_EMPTY_MAP = new ImmutableAnyMapImpl(INSTANCE.createAnyMap()); + + /** + * {@inheritDoc} + */ + @Override + public AnyMap createAnyMap() { + return new AnyMapImpl(); + } + + /** + * {@inheritDoc} + */ + @Override + public AnySeq createAnySeq() { + return new AnySeqImpl(); + } + + /** {@inheritDoc} */ + @Override + public Value createStringValue(final String value) { + return new ValueImpl(ValueType.STRING, value); + } + + /** {@inheritDoc} */ + @Override + public Value createBooleanValue(final Boolean value) { + return new ValueImpl(ValueType.BOOLEAN, value); + } + + /** {@inheritDoc} */ + @Override + public Value createLongValue(final Long value) { + return new ValueImpl(ValueType.LONG, value); + } + + /** {@inheritDoc} */ + @Override + public Value createLongValue(final int value) { + return new ValueImpl(ValueType.LONG, (long) value); + } + + /** {@inheritDoc} */ + @Override + public Value createDoubleValue(final Double value) { + return new ValueImpl(ValueType.DOUBLE, value); + } + + /** {@inheritDoc} */ + @Override + public Value createDoubleValue(final float value) { + return new ValueImpl(ValueType.DOUBLE, Double.valueOf(value)); + } + + /** {@inheritDoc} */ + @Override + public Value createDateValue(final Date value) { + return new ValueImpl(ValueType.DATE, new Date(value.getTime())); + } + + /** create DATE value with original string. */ + private Value createDateValue(final Date value, final String originalString) { + return new ValueImpl(ValueType.DATE, new DateValue(new Date(value.getTime()), originalString)); + } + + /** {@inheritDoc} */ + @Override + public Value createDateTimeValue(final Date value) { + return new ValueImpl(ValueType.DATETIME, new Date(value.getTime())); + } + + /** create DATETIME value with original string. */ + private Value createDateTimeValue(final Date value, final String originalString) { + return new ValueImpl(ValueType.DATETIME, new DateValue(new Date(value.getTime()), originalString)); + } + + /** {@inheritDoc} */ + @Override + public Value parseFromString(final String value, final String type) { + ValueType valueType = null; + if (type == null) { + valueType = ValueType.STRING; + } else { + try { + valueType = ValueType.valueOf(type.toUpperCase()); + } catch (final IllegalArgumentException e) { + throw new InvalidValueTypeException("invalid type: " + type); + } + } + return parseFromString(value, valueType); + } + + /** {@inheritDoc} */ + @Override + public Value parseFromString(final String value, final ValueType valueType) { + try { + switch (valueType) { + case BOOLEAN: + return createBooleanValue(Boolean.parseBoolean(value)); + case DATE: + return createDateValue(ValueFormatHelper.INSTANCE.parseDate(value), value); + case DATETIME: + return createDateTimeValue(ValueFormatHelper.INSTANCE.parseDateTime(value), value); + case DOUBLE: + return createDoubleValue(Double.parseDouble(value)); + case LONG: + return createLongValue(Long.parseLong(value)); + case STRING: + return createStringValue(value); + default: + throw new NotImplementedException("conversion for type: " + valueType); + } + } catch (final Exception e) { + throw new InvalidValueTypeException(format("cannot convert value %s into %s", value, valueType), e); + } + } + + /** {@inheritDoc} */ + @Override + public Value tryDateTimestampParsingFromString(final String value) { + final Date timestamp = ValueFormatHelper.INSTANCE.tryParseDateTime(value); + if (timestamp != null) { + return createDateTimeValue(timestamp, value); + } + final Date date = ValueFormatHelper.INSTANCE.tryParseDate(value); + if (date != null) { + return createDateValue(date, value); + } + return createStringValue(value); + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@link #autoConvertValue(Object)} instead + */ + @Deprecated + @Override + public Value parseFromObject(final Object object) { + return autoConvertValue(object); + } + + /** {@inheritDoc} */ + @Override + public Value autoConvertValue(final Object object) { + if (object instanceof Value) { + return (Value) object; + } else if (object instanceof String) { + return createStringValue((String) object); + } else if (object instanceof Double) { + return createDoubleValue((Double) object); + } else if (object instanceof Long) { + return createLongValue((Long) object); + } else if (object instanceof Integer) { + return createLongValue(((Integer) object).longValue()); + } else if (object instanceof Short) { + return createLongValue(((Short) object).longValue()); + } else if (object instanceof Byte) { + return createLongValue(((Byte) object).longValue()); + } else if (object instanceof Number) { + return createDoubleValue(((Number) object).doubleValue()); + } else if (object instanceof Boolean) { + return createBooleanValue((Boolean) object); + } else if (object instanceof Date) { + return createDateTimeValue((Date) object); + } else { + throw new InvalidValueTypeException(object.getClass()); + } + } + + /** {@inheritDoc} */ + @Override + public Any cloneAny(final Any source) { + if (source == null) { + return null; + } + switch (source.getValueType()) { + case MAP: + return cloneAnyMap((AnyMap) source); + case SEQ: + return cloneAnySeq((AnySeq) source); + default: + return new ValueImpl((Value) source); + } + } + + /** {@inheritDoc} */ + @Override + public AnyMap cloneAnyMap(final AnyMap source) { + if (source == null) { + return null; + } + final AnyMap destination = createAnyMap(); + for (final Entry entry : source.entrySet()) { + destination.put(entry.getKey(), cloneAny(entry.getValue())); + } + return destination; + } + + /** {@inheritDoc} */ + @Override + public AnySeq cloneAnySeq(final AnySeq source) { + if (source == null) { + return null; + } + final AnySeq destination = createAnySeq(); + final Iterator iterator = source.iterator(); + while (iterator.hasNext()) { + destination.add(cloneAny(iterator.next())); + } + return destination; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/ImmutableAnyMapImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/ImmutableAnyMapImpl.java new file mode 100644 index 0000000..707ee5d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/ImmutableAnyMapImpl.java @@ -0,0 +1,257 @@ +package eu.dnetlib.functionality.index.model.impl; + +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import eu.dnetlib.functionality.index.model.Any; +import eu.dnetlib.functionality.index.model.AnyMap; +import eu.dnetlib.functionality.index.model.AnySeq; +import eu.dnetlib.functionality.index.model.DataFactory; +import eu.dnetlib.functionality.index.model.Value; + +/** + * immutable decorator for an AnyMap. + */ +public class ImmutableAnyMapImpl implements AnyMap { + + /** The underlying anymap. */ + private final AnyMap _anyMap; + + /** The underlying map, as immutable. */ + private final Map _immutable; + + public ImmutableAnyMapImpl(AnyMap map) { + _anyMap = map; + _immutable = Collections.unmodifiableMap(map); + } + + @Override + public void add(String key, Any value) { + throw new UnsupportedOperationException(); + } + + @Override + public AnyMap asMap() { + return _anyMap.asMap(); + } + + @Override + public AnySeq asSeq() { + return _anyMap.asSeq(); + } + + @Override + public Value asValue() { + return _anyMap.asValue(); + } + + @Override + public void clear() { + _immutable.clear(); + } + + @Override + public boolean containsKey(Object key) { + return _immutable.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return _immutable.containsValue(value); + } + + @Override + public Set> entrySet() { + return _immutable.entrySet(); + } + + @Override + public boolean equals(Object o) { + return _immutable.equals(o); + } + + @Override + public Any get(Object key) { + return _immutable.get(key); + } + + @Override + public Boolean getBooleanValue(String key) { + return _anyMap.getBooleanValue(key); + } + + @Override + public Date getDateTimeValue(String key) { + return _anyMap.getDateTimeValue(key); + } + + @Override + public Date getDateValue(String key) { + return _anyMap.getDateValue(key); + } + + @Override + public Double getDoubleValue(String key) { + return _anyMap.getDoubleValue(key); + } + + @Override + public DataFactory getFactory() { + return _anyMap.getFactory(); + } + + @Override + public Long getLongValue(String key) { + return _anyMap.getLongValue(key); + } + + @Override + public AnyMap getMap(String key) { + return _anyMap.getMap(key); + } + + @Override + public AnyMap getMap(String key, boolean create) { + return _anyMap.getMap(key, create); + } + + @Override + public AnySeq getSeq(String key) { + return _anyMap.getSeq(key); + } + + @Override + public AnySeq getSeq(String key, boolean create) { + return _anyMap.getSeq(key, create); + } + + @Override + public String getStringValue(String key) { + return _anyMap.getStringValue(key); + } + + @Override + public Value getValue(String key) { + return _anyMap.getValue(key); + } + + @Override + public ValueType getValueType() { + return _anyMap.getValueType(); + } + + @Override + public int hashCode() { + return _immutable.hashCode(); + } + + @Override + public boolean isBoolean() { + return _anyMap.isBoolean(); + } + + @Override + public boolean isDate() { + return _anyMap.isDate(); + } + + @Override + public boolean isDateTime() { + return _anyMap.isDateTime(); + } + + @Override + public boolean isDouble() { + return _anyMap.isDouble(); + } + + @Override + public boolean isEmpty() { + return _immutable.isEmpty(); + } + + @Override + public boolean isLong() { + return _anyMap.isLong(); + } + + @Override + public boolean isMap() { + return _anyMap.isMap(); + } + + @Override + public boolean isNumber() { + return _anyMap.isNumber(); + } + + @Override + public boolean isSeq() { + return _anyMap.isSeq(); + } + + @Override + public boolean isString() { + return _anyMap.isString(); + } + + @Override + public boolean isValue() { + return _anyMap.isValue(); + } + + @Override + public Iterator iterator() { + return _anyMap.iterator(); + } + + @Override + public Set keySet() { + return _immutable.keySet(); + } + + @Override + public Any put(String key, Any value) { + throw new UnsupportedOperationException(); + } + + @Override + public Any put(String key, Boolean value) { + throw new UnsupportedOperationException(); + } + + @Override + public Any put(String key, Number value) { + throw new UnsupportedOperationException(); + } + + @Override + public Any put(String key, String value) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException(); + } + + @Override + public Any remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + return _immutable.size(); + } + + @Override + public Collection values() { + return _immutable.values(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/ValueImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/ValueImpl.java new file mode 100755 index 0000000..c35379a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/impl/ValueImpl.java @@ -0,0 +1,258 @@ +package eu.dnetlib.functionality.index.model.impl; + +import java.text.ParseException; +import java.util.Date; +import java.util.Iterator; + +import com.google.common.collect.Iterators; + +import eu.dnetlib.functionality.index.model.Any; +import eu.dnetlib.functionality.index.model.AnySeq; +import eu.dnetlib.functionality.index.model.InvalidValueTypeException; +import eu.dnetlib.functionality.index.model.Value; +import eu.dnetlib.functionality.index.model.ValueFormatHelper; + +/** + * Any holding a value. + */ +public final class ValueImpl extends AbstractAny implements Value { + + /** version. */ + private static final long serialVersionUID = 1L; + + /** + * the value object. + */ + private final Object _value; + + /** + * create new empty ValueImpl instance. + * + * @param valueType + * the type of the Value + * @param value + * the value of the Value + */ + ValueImpl(final ValueType valueType, final Object value) { + super(valueType); + if (value == null) throw new IllegalArgumentException("The value of any Any must not be null."); + if (value instanceof Date) { + _value = new DateValue((Date) value); + } else { + _value = value; + } + } + + /** + * create new empty ValueImpl instance as a copy of a given Value. + * + * @param object + * the vlaue to be copied + */ + ValueImpl(final Value object) { + super(object.getValueType()); + if (object instanceof ValueImpl) { + _value = ((ValueImpl) object)._value; + } else { + switch (object.getValueType()) { + case BOOLEAN: + _value = object.asBoolean(); + break; + case DATE: + _value = new DateValue(object.asDate()); + break; + case DATETIME: + _value = new DateValue(object.asDateTime()); + break; + case DOUBLE: + _value = object.asDouble(); + break; + case LONG: + _value = object.asLong(); + break; + case STRING: + _value = object.asString(); + break; + default: + throw new InvalidValueTypeException("Value of type '" + object.getValueType() + "' cannot be copied."); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public Double asDouble() { + if (_valueType == ValueType.DOUBLE) return (Double) _value; + else if (_valueType == ValueType.LONG) return new Double(((Number) _value).doubleValue()); + else if (_valueType == ValueType.STRING) { + try { + return Double.valueOf((String) _value); + } catch (final NumberFormatException e) { + throw new InvalidValueTypeException("Value '" + _value + "' of type '" + _valueType + "' cannot be converted to double."); + } + } else throw new InvalidValueTypeException("Value of type '" + _valueType + "' cannot be converted to double."); + } + + /** + * {@inheritDoc} + */ + @Override + public Long asLong() { + if (_valueType == ValueType.LONG) return (Long) _value; + else if (_valueType == ValueType.DOUBLE) return Long.valueOf(((Number) _value).longValue()); + else if (_valueType == ValueType.STRING) { + try { + return Long.valueOf((String) _value); + } catch (final NumberFormatException e) { + throw new InvalidValueTypeException("Value '" + _value + "' of type '" + _valueType + "' cannot be converted to long."); + } + } else throw new InvalidValueTypeException("Value of type '" + _valueType + "' cannot be converted to long."); + } + + /** {@inheritDoc} */ + @Override + public Boolean asBoolean() { + if (_valueType == ValueType.BOOLEAN) return (Boolean) _value; + else if (_valueType == ValueType.STRING) { + try { + return Boolean.valueOf((String) _value); + } catch (final NumberFormatException e) { + throw new InvalidValueTypeException("Value '" + _value + "' of type '" + _valueType + "' cannot be converted to boolean."); + } + } else throw new InvalidValueTypeException("Value of type '" + _valueType + "' cannot be converted to boolean."); + } + + /** {@inheritDoc} */ + @Override + public Date asDate() { + if ((_valueType == ValueType.DATE) || (_valueType == ValueType.DATETIME)) return new Date(((DateValue) _value).getTime()); + else if (_valueType == ValueType.STRING) { + try { + return ValueFormatHelper.INSTANCE.parseDate((String) _value); + } catch (final ParseException e) { + throw new InvalidValueTypeException("Value '" + _value + "' of type '" + _valueType + "' cannot be converted to date."); + } + } else throw new InvalidValueTypeException("Value of type '" + _valueType + "' cannot be converted to Date."); + } + + /** {@inheritDoc} */ + @Override + public Date asDateTime() { + if ((_valueType == ValueType.DATE) || (_valueType == ValueType.DATETIME)) return new Date(((DateValue) _value).getTime()); + else if (_valueType == ValueType.STRING) { + try { + return ValueFormatHelper.INSTANCE.parseDateTime((String) _value); + } catch (final ParseException e) { + throw new InvalidValueTypeException("Value '" + _value + "' of type '" + _valueType + "' cannot be converted to datetime."); + } + } else throw new InvalidValueTypeException("Value of type '" + _valueType + "' cannot be converted to DateTime."); + } + + /** {@inheritDoc} */ + @Override + public String asString() { + if (_value == null) return null; + else { + switch (_valueType) { + case STRING: + return (String) _value; + case DATE: + case DATETIME: + return getDateString((DateValue) _value); + default: + return _value.toString(); + } + } + } + + /** get original string from {@link DateValue}, if exists, else format the contained date as date or timestamp. */ + private String getDateString(final DateValue pair) { + if (pair.hasOriginalString()) return pair.getOriginalString(); + if (_valueType == ValueType.DATE) return ValueFormatHelper.INSTANCE.formatDate(pair.getDate()); + return ValueFormatHelper.INSTANCE.formatDateTime(pair.getDate()); + } + + /** {@inheritDoc} */ + @Override + public Object getObject() { + if ((_valueType == ValueType.DATE) || (_valueType == ValueType.DATETIME)) return ((DateValue) _value).getDate(); + return _value; + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + if (_value == null) return "(void)"; + return asString(); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + (_value == null ? 0 : _value.hashCode()); + result = (prime * result) + (_valueType == null ? 0 : _valueType.hashCode()); + return result; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final ValueImpl other = (ValueImpl) obj; + if (_value == null) { + if (other._value != null) return false; + } else if (!_value.equals(other._value)) return false; + if (_valueType == null) { + if (other._valueType != null) return false; + } else if (!_valueType.equals(other._valueType)) return false; + return true; + } + + /** {@inheritDoc} */ + @Override + public Iterator iterator() { + return Iterators.unmodifiableIterator(this.iterator()); + } + + /** {@inheritDoc} */ + @Override + public boolean isEmpty() { + return false; + } + + /** {@inheritDoc} */ + @Override + public int size() { + return 1; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.smila.datamodel.Any#asValue() + */ + @Override + public Value asValue() { + return this; + } + + @Override + public AnySeq asSeq() { + final AnySeq seq = new AnySeqImpl(); + seq.add(this); + return seq; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/util/AnySolrUtil.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/util/AnySolrUtil.java new file mode 100644 index 0000000..0782cc6 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/util/AnySolrUtil.java @@ -0,0 +1,69 @@ +package eu.dnetlib.functionality.index.model.util; + +import java.util.Iterator; +import java.util.Map.Entry; + +import eu.dnetlib.functionality.index.model.AnyMap; +import eu.dnetlib.functionality.index.model.DataFactory; +import eu.dnetlib.functionality.index.model.InvalidValueTypeException; +import eu.dnetlib.functionality.index.model.Value; +import eu.dnetlib.functionality.index.model.impl.DefaultDataFactoryImpl; +import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.util.NamedList; + +/** + * The Class AnySolrUtil. + */ +public class AnySolrUtil extends AnyUtil { + + /** + * Convert named list to any map. + * + * @param list + * the list + * @param map + * the map + * @return the any map + */ + @SuppressWarnings("unchecked") + public static AnyMap convertNamedListToAnyMap(final NamedList list, final AnyMap map) { + final Iterator> it = list.iterator(); + while (it.hasNext()) { + Entry entry = it.next(); + final String key = entry.getKey(); + final Object obj = entry.getValue(); + if (obj instanceof NamedList) { + final AnyMap subMap = map.getMap(key, true); + convertNamedListToAnyMap((NamedList) obj, subMap); + } else if (obj instanceof SolrDocumentList) { + SolrDocumentList docList = (SolrDocumentList) obj; + AnyMap response = DataFactory.DEFAULT.createAnyMap(); + response.put("numFound", docList.getNumFound()); + response.put("start", docList.getStart()); + response.put("maxScore", docList.getMaxScore()); + response.put("docs", objectToAny(obj)); + map.put("response", response); + } else { + try { + final Value value = DataFactory.DEFAULT.autoConvertValue(obj); + map.put(key, value); + } catch (InvalidValueTypeException exception) { + ; // skip + } + } + } + return map; + } + + /** + * Convert named list to any map. + * + * @param list + * the list + * @return the any map + */ + public static AnyMap convertNamedListToAnyMap(final NamedList list) { + return convertNamedListToAnyMap(list, DefaultDataFactoryImpl.INSTANCE.createAnyMap()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/util/AnyUtil.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/util/AnyUtil.java new file mode 100644 index 0000000..49ef000 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/model/util/AnyUtil.java @@ -0,0 +1,310 @@ +package eu.dnetlib.functionality.index.model.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; + +import eu.dnetlib.functionality.index.model.Any; +import eu.dnetlib.functionality.index.model.AnyMap; +import eu.dnetlib.functionality.index.model.AnySeq; +import eu.dnetlib.functionality.index.model.DataFactory; +import eu.dnetlib.functionality.index.model.Value; +import eu.dnetlib.functionality.index.model.impl.DefaultDataFactoryImpl; + +/** + * utility class for handling / conversion of Any objects. + * + * Hint: The Any-to-JSON conversion for Date(Time)s is not symmetric. If the Any object contains Date(Time) values, they will be serialized + * to String values in simple timestamp format. But when parsing JSON to an Any object, Date(Time) strings will not be recognized anymore, + * but just read as String values. + */ +public class AnyUtil { + + /** Immutable empty AnyMap instance. */ + public static final AnyMap EMPTY_MAP = DefaultDataFactoryImpl.IMMUTABLE_EMPTY_MAP; + + /** + * prevent instance creation. + */ + protected AnyUtil() { + // prevent instance creation + } + + /** + * Converts an Any object into a native java object. + * + * @param any + * the Any object + * @return a Pojo + */ + public static Object anyToNative(final Any any) { + if (any.isMap()) { + final LinkedHashMap map = new LinkedHashMap(); + for (final Iterator kIt = ((AnyMap) any).keySet().iterator(); kIt.hasNext();) { + final String key = kIt.next(); + map.put(key, anyToNative(((AnyMap) any).get(key))); + } + return map; + } else if (any.isSeq()) { + final ArrayList list = new ArrayList(); + for (final Iterator aIt = any.iterator(); aIt.hasNext();) { + final Any a = aIt.next(); + list.add(anyToNative(a)); + } + return list; + } else if (any.isString()) return ((Value) any).asString(); + else if (any.isLong()) return ((Value) any).asLong(); + else if (any.isDouble()) return ((Value) any).asDouble(); + else if (any.isBoolean()) return ((Value) any).asBoolean(); + else if (any.isDate()) return ((Value) any).asDate(); + else if (any.isDateTime()) return ((Value) any).asDateTime(); + else return ((Value) any).getObject(); + } + + /** + * Converts an object to an Any (recursively). The leaf object(s) must be convertable by {@link DataFactory#autoConvertValue(Object)}. + * + * @param object + * The object to be converted. Supported (and tested in this order) are + *
    + *
  • {@code Map} + *
  • {@code Collections} + *
  • {@code Object[]} + *
  • Any other object that can be {@link DataFactory#autoConvertValue(Object)}
  • + * + * + * @return The converted Any + */ + @SuppressWarnings("unchecked") + public static Any objectToAny(final Object object) { + Any value = null; + if (object instanceof Any) return (Any) object; + else if (object instanceof Map) { + value = mapToAny((Map) object); + } else if (object instanceof Collection) { + value = collectionToAny((Collection) object); + } else if (object.getClass().isArray()) { + Object[] array = (Object[]) object; + value = collectionToAny(Arrays.asList(array)); + } else { + value = scalarObjectToAny(object); + } + return value; + } + + /** + * Converts a collection to an AnySeq object. + * + * @param objects + * The list of objects to convert. + * @return An AnySeq containing the objects as Any objects. + */ + private static AnySeq collectionToAny(final Collection objects) { + AnySeq anySeq = null; + if (objects != null) { + anySeq = DataFactory.DEFAULT.createAnySeq(); + for (final Object obj : objects) { + anySeq.add(objectToAny(obj)); + } + } + return anySeq; + } + + /** + * Converts a scalar object to a Value object. + * + * @param obj + * The object to convert. + * @return A Value representing the object. + */ + private static Any scalarObjectToAny(final Object obj) { + return DataFactory.DEFAULT.autoConvertValue(obj); + } + + /** + * Converts a map to an AnyMap object. + * + * @param map + * The map (String to Object) to convert. + * @return An AnyMap representing the map with all Objects converted to Any. + */ + private static AnyMap mapToAny(final Map map) { + AnyMap anyMap = null; + if (map != null) { + anyMap = DataFactory.DEFAULT.createAnyMap(); + for (final Entry entry : map.entrySet()) { + anyMap.put(entry.getKey(), objectToAny(entry.getValue())); + } + } + return anyMap; + } + + /** + * get value for given path(list of keys) from AnyMap object. This methods throws no exception, if the path not exists an empty Any is + * the result. + * + * @param any + * the Any object. + * @param path + * path to the entry. + * @return value associated to the path. + */ + public static Any saveGet(final Any any, final String[] path) { + if (path.length == 0) return DataFactory.DEFAULT.createAnyMap(); + try { + Any current = any; + for (final String key : path) { + if (current.isMap()) { + current = ((AnyMap) current).get(key); + } else { + current = null; + } + } + if (current == null) return DataFactory.DEFAULT.createStringValue("undef"); + else return current; + } catch (final Exception e) { + return DataFactory.DEFAULT.createStringValue("undef"); + } + } + + /** + * convert an exception to an any object. + * + * @param e + * exception to convert + * @return any representation of exception + */ + public static AnyMap exceptionToAny(final Throwable e) { + return exceptionToAny(e, new HashSet()); + } + + /** + * convert an exception to an any object. stop in stacktrace printing when hitting known lines again. + * + * @param e + * exception to convert + * @param visitedLines + * lines that have been added to stacktraces before. + * @return any representation of exception + */ + private static AnyMap exceptionToAny(final Throwable e, final Collection visitedLines) { + final AnyMap any = DataFactory.DEFAULT.createAnyMap(); + any.put("type", e.getClass().getName()); + if (e.getMessage() != null) { + any.put("message", e.getMessage()); + } + final AnySeq st = DataFactory.DEFAULT.createAnySeq(); + for (final StackTraceElement stElement : e.getStackTrace()) { + final String line = stElement.toString(); + st.add(line); + if (!visitedLines.add(line)) { + st.add("..."); + break; + } + } + any.put("at", st); + if ((e.getCause() != null) && (e.getCause() != e)) { + any.put("causedBy", exceptionToAny(e.getCause(), visitedLines)); + } + return any; + } + + /** + * null save version. + */ + public static Double asDouble(final Any any) { + return any == null ? null : any.asValue().asDouble(); + }; + + /** + * null save version. + */ + public static Boolean asBoolean(final Any any) { + return any == null ? null : any.asValue().asBoolean(); + }; + + /** + * null save version. + */ + public static Date asDateTime(final Any any) { + return any == null ? null : any.asValue().asDateTime(); + }; + + /** + * null save version. + */ + public static Date asDate(final Any any) { + return any == null ? null : any.asValue().asDate(); + }; + + /** + * null save version. + */ + public static Long asLong(final Any any) { + return any == null ? null : any.asValue().asLong(); + }; + + /** + * null save version. + */ + public static String asString(final Any any) { + return any == null ? null : any.asValue().asString(); + }; + + /** + * null save version. + */ + public static AnyMap asMap(final Any any) { + return any == null ? null : any.asMap(); + }; + + /** + * null save version. + */ + public static AnySeq asSeq(final Any any) { + return any == null ? null : any.asSeq(); + }; + + /** convert AnyMap to java.util.Properties. */ + public static Properties anyToProperties(final AnyMap anyMap) { + Properties props = new Properties(); + final Set keySet = anyMap.keySet(); + for (final String key : keySet) { + props.put(key, anyMap.get(key).toString()); + } + return props; + } + + /** convert java.util.Properties to AnyMap. */ + public static AnyMap propertiesToAny(final Properties props) { + final AnyMap any = DataFactory.DEFAULT.createAnyMap(); + final Set propNames = props.stringPropertyNames(); + for (final String prop : propNames) { + any.put(prop, props.getProperty(prop)); + } + return any; + } + + /** + * returns the 1st map in the give SEQ that contains a value with the given name value, or null if not found. + * + * This is often useful for configs that are contained in a seq such as search filters. + * + * @since 1.1 + */ + public static AnyMap findMapInSeq(final AnySeq seq, final String keyName, final String keyValue) { + for (Any any : seq) { + final String stringValue = any.asMap().getStringValue(keyName); + if (keyValue.equals(stringValue)) return any.asMap(); + } + return null; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/BrowseAliases.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/BrowseAliases.java new file mode 100644 index 0000000..bbd5192 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/BrowseAliases.java @@ -0,0 +1,133 @@ +package eu.dnetlib.functionality.index.query; + +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Document; +import org.dom4j.Element; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Required; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Maps; + +import eu.dnetlib.data.provision.index.rmi.IndexServiceException; +import eu.dnetlib.functionality.index.client.IndexClientException; +import eu.dnetlib.functionality.index.utils.IndexFieldUtility; +import eu.dnetlib.functionality.index.utils.MDFormatReader; +import eu.dnetlib.functionality.index.utils.MetadataReference; +import eu.dnetlib.functionality.index.utils.ServiceTools; + +/** + * The Class BrowseAliases. + */ +public class BrowseAliases { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(BrowseAliases.class); // NOPMD + + /** The aliases. */ + private Map> aliases = Maps.newConcurrentMap(); + + private ServiceTools serviceTools; + + @Autowired + private MDFormatReader mdFormatReader; + + /** + * Initialize. + * + * @throws IndexServiceException + * the index service exception + */ + public void initialize() throws IndexClientException { + log.info("initializing browse aliases"); + for (MetadataReference mdRef : getServiceTools().listMDRefs()) { + log.debug("inside foreach"); + put(mdRef); + } + log.info("browse aliases initialization completed"); + } + + /** + * Put. + * + * @param mdRef + * the metadata refeference + */ + public void put(final MetadataReference mdRef) { + final Document fields = mdFormatReader.getFields(mdRef); + if (fields != null) { + aliases.put(mdRef, extractBrowsingAliases(fields)); + } else { + // log.info("couldn't find any"); + BiMap m = HashBiMap.create(); + aliases.put(mdRef, m); + } + } + + /** + * Gets the. + * + * @param mdRef + * the md ref + * @return browsing aliases for given mdRef. + * @throws IndexClientException + */ + public BiMap get(final MetadataReference mdRef) throws IndexClientException { + if ((aliases == null) || (aliases.size() == 0)) { + initialize(); + } + return aliases.get(mdRef); + } + + /** + * Method extract aliases field names from the given fields. + * + * @param fields + * the fields + * @return aliases map. Keys are "normal" field names, values are names of the non-tokenized version of the field + */ + protected BiMap extractBrowsingAliases(final Document fields) { + // default tokenizer splits field names, this would cause to + // have too many browsing results, so we use an untokenized + // alias in place of it. + + final BiMap aliases = HashBiMap.create(); + + @SuppressWarnings("unchecked") + final List fieldList = fields.getRootElement().selectNodes(IndexFieldUtility.XPATH_BROWSING_ALIAS_FOR); + for (final Element e : fieldList) { + final String name = e.attribute(IndexFieldUtility.FIELD_BROWSING_ALIAS_FOR).getValue().toLowerCase(); + final String alias = e.attribute(IndexFieldUtility.FIELD_NAME).getValue().toLowerCase(); + aliases.put(name, alias); + } + + if (aliases.isEmpty()) { + log.warn("couldn'f find alias fields for browsing"); + } + return aliases; + } + + /** + * @return the serviceTools + */ + public ServiceTools getServiceTools() { + return serviceTools; + } + + /** + * @param serviceTools + * the serviceTools to set + */ + @Required + public void setServiceTools(final ServiceTools serviceTools) { + this.serviceTools = serviceTools; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/IndexQuery.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/IndexQuery.java new file mode 100644 index 0000000..84db215 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/IndexQuery.java @@ -0,0 +1,12 @@ +package eu.dnetlib.functionality.index.query; + +/** + * The Class IndexQuery. + */ +public interface IndexQuery { + + IndexQuery setQueryOffset(int offset); + + IndexQuery setQueryLimit(int limit); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/IndexQueryFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/IndexQueryFactory.java new file mode 100644 index 0000000..cf4e2d1 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/IndexQueryFactory.java @@ -0,0 +1,200 @@ +package eu.dnetlib.functionality.index.query; + +import java.util.List; +import java.util.Map; + +import com.google.common.collect.BiMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import eu.dnetlib.data.provision.index.rmi.IndexServiceException; +import eu.dnetlib.functionality.cql.CqlTranslator; +import eu.dnetlib.functionality.cql.lucene.TranslatedQuery; +import eu.dnetlib.functionality.index.client.IndexClient; +import eu.dnetlib.functionality.index.client.IndexClientException; +import eu.dnetlib.functionality.index.query.Pruner.Result; +import eu.dnetlib.functionality.index.utils.MetadataReference; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * A factory for creating IndexQuery objects. + */ +public abstract class IndexQueryFactory { + + /** + * Query tree pruner. + */ + private Pruner pruner; + + /** + * Query tree pruner. Collects parameters which affect the semantic of the cql parser. + */ + private Pruner cqlPruner; + + /** The default query params. */ + private Map> defaultQueryParams; + + /** CqlTranslator. */ + @Autowired + private CqlTranslator translator; + + /** The browse aliases. */ + @Autowired + private BrowseAliases browseAliases; + + /** The weights. */ + @Autowired + private Weights weights; + + /** + * New instance. + * + * @param cql + * the cql + * @param res + * the res + * @param queryLanguage + * the query language + * @return the index query + */ + protected abstract IndexQuery newInstance(final TranslatedQuery cql, final Result res, final QueryLanguage queryLanguage); + + /** + * Sets the query options. + * + * @param indexQuery + * the index query + * @return the index query + */ + protected abstract IndexQuery setQueryOptions(final IndexQuery indexQuery, final IndexClient client); + + /** + * Gets the index query. + * + * @param lang + * the lang + * @param query + * the query + * @param mdRef + * the md ref + * @return the index query + * @throws IndexServiceException + * the index service exception + */ + public IndexQuery getIndexQuery(final QueryLanguage lang, final String query, final IndexClient client, final MetadataReference mdRef) + throws IndexClientException { + + String myquery = query; + + if ((myquery == null) || myquery.isEmpty()) throw new IndexClientException("query cannot be empty or null"); + + try { + final Result cqlRes = getCqlPruner().prune(getCqlPruner().parse(myquery)); + final Result res = getPruner().prune(cqlRes.getNode()); + + final TranslatedQuery tQuery = translator.getTranslatedQuery(res.getNode(), client.getCqlValueTransformerMap(mdRef), + overrideCqlParams(cqlRes.getOptionMap()), browseAliases.get(mdRef), weights.get(mdRef)); + + return setQueryOptions(newInstance(tQuery, res, lang), client); + } catch (Exception e) { + throw new IndexClientException(e); + } + } + + /** + * Method overrides the default values in the defaultQueryParams with the given override map. + * + * @param override + * the map containing the override values. + * @return the overridden parameter map + * + */ + private Map> overrideCqlParams(final Map> override) { + Map> cqlParams = Maps.newHashMap(); + cqlParams.putAll(getDefaultQueryParams()); + cqlParams.putAll(override); + return cqlParams; + } + + public List getBrowsableFields(final List fields, final MetadataReference mdRef) throws IndexClientException { + return getBrowsableFields(fields, browseAliases.get(mdRef)); + } + + /** + * Gets the list of aliases available for browse + * + * @param fields + * list of input fields + * @param aliases + * key= non-browasbale-field-name, value=browsable-field-name + * @return the list of browasable field names + */ + public List getBrowsableFields(final List fields, final BiMap aliases) { + List browsables = Lists.newArrayListWithExpectedSize(fields.size()); + for (String f : fields) { + if (aliases.containsKey(f)) { + browsables.add(aliases.get(f)); + } else { + browsables.add(f); + } + } + return browsables; + } + + /** + * Gets the pruner. + * + * @return the pruner + */ + public Pruner getPruner() { + return pruner; + } + + /** + * Sets the pruner. + * + * @param pruner + * the new pruner + */ + public void setPruner(final Pruner pruner) { + this.pruner = pruner; + } + + /** + * Gets the cql pruner. + * + * @return the cql pruner + */ + public Pruner getCqlPruner() { + return cqlPruner; + } + + /** + * Sets the cql pruner. + * + * @param cqlPruner + * the new cql pruner + */ + public void setCqlPruner(final Pruner cqlPruner) { + this.cqlPruner = cqlPruner; + } + + /** + * Gets the default query params. + * + * @return the default query params + */ + public Map> getDefaultQueryParams() { + return defaultQueryParams; + } + + /** + * Sets the default query params. + * + * @param defaultQueryParams + * the default query params + */ + public void setDefaultQueryParams(final Map> defaultQueryParams) { + this.defaultQueryParams = defaultQueryParams; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/IndexQueryResponse.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/IndexQueryResponse.java new file mode 100644 index 0000000..d38ab47 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/IndexQueryResponse.java @@ -0,0 +1,7 @@ +package eu.dnetlib.functionality.index.query; + +public interface IndexQueryResponse { + + public T getContextualQueryResponse(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/Pruner.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/Pruner.java new file mode 100644 index 0000000..c1b749a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/Pruner.java @@ -0,0 +1,319 @@ +package eu.dnetlib.functionality.index.query; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.z3950.zing.cql.*; + +/** + * Use this class to cleanup a CQL tree and obtain all the options + * + * @author marko & claudio + * + */ +public class Pruner { + private static final Log log = LogFactory.getLog(Pruner.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * All options have to be in this namespace. + */ + public static final String DNET_URI = "NAMESPACE"; + + private String optionUri = DNET_URI; + + /** + * Helper method, parse a given CQL string. + * + * @param cqlQuery + * @return the parsed CQLNode + * @throws CQLParseException + * @throws IOException + */ + CQLNode parse(final String cqlQuery) throws CQLParseException, IOException { + return new CQLParser().parse(cqlQuery); + } + + class Result { + private CQLNode node; + private List options; + + public Result(final CQLNode node, final List options) { + super(); + this.node = node; + this.options = options; + } + + public Result(final CQLNode node, final Iterable concat) { + this.node = node; + this.options = Lists.newArrayList(concat); + } + + public CQLNode getNode() { + return node; + } + + public void setNode(final CQLNode node) { + this.node = node; + } + + public List getOptions() { + return options; + } + + public void setOptions(final List options) { + this.options = options; + } + + public Map> getOptionMap() { + Map> res = new HashMap>(); + for (String opt : options) { + String[] k = opt.split("="); + List l = res.get(k[0]); + if(l == null) + l = new ArrayList(); + l.add(k[1]); + res.put(k[0], l); + } + return res; + } + } + + /** + * Remove all options from a given CQL AST and return all the options. + * + * The CQL tree is modified. + * + * @param root + * cql tree + * @return pair containing a new root node and a list of options + */ + public Result prune(final CQLNode root) { + return prune(new HashMap(), root); + } + + /** + * Actual recursive implementation, dispatches the implementation to the appropriate overloaded method. + * + * @param prefixes + * @param root + * @return the pruned result + */ + public Result prune(final Map prefixes, final CQLNode root) { + + if (root instanceof CQLBooleanNode) + return prune(prefixes, (CQLBooleanNode) root); + + if (root instanceof CQLPrefixNode) + return prune(prefixes, (CQLPrefixNode) root); + + if (root instanceof CQLSortNode) + return prune(prefixes, (CQLSortNode) root); + + return new Result(root, new ArrayList()); + } + + /** + * If the current node is a cql "sort" node, just return the inner subtree. + * + * @param prefixes + * @param node + * @return the pruned result + */ + public Result prune(final Map prefixes, final CQLSortNode node) { + Result res = prune(prefixes, node.subtree); + node.subtree = res.getNode(); + res.setNode(node); + return res; + } + + /** + * If the current node is a cql "prefix" node, add his namespace declaration to the current list of namespaces and + * return the pruned inner subtree. + * + * If the prefix node contains only one single option element, we have to return null. (TODO: perhaps there is a + * better solution). + * + * @param prefixes + * @param node + * @return the pruned result + */ + public Result prune(final Map prefixes, final CQLPrefixNode node) { + final HashMap subPrefixes = Maps.newHashMap(prefixes); + subPrefixes.put(node.prefix.name, node.prefix.identifier); + + if (isOption(subPrefixes, node.subtree)) + return new Result(null, Lists.newArrayList(getOption(node.subtree))); + + boolean pruneThisPrefix = node.prefix.identifier.equals(optionUri); + if(pruneThisPrefix) + return prune(subPrefixes, node.subtree); + + Result res = prune(subPrefixes, node.subtree); + node.subtree = res.getNode(); + res.setNode(node); + return res; + + } + + /** + * boolean prunes are handled in the prune(prefix, node, left, right). + * + * @param prefixes + * @param node + * @return the pruned result + */ + public Result prune(final Map prefixes, final CQLBooleanNode node) { + return prune(prefixes, node, node.left, node.right); + } + + /** + * Detects if a left or right side of a boolean node is a option term, and returns the other side (recursively + * pruned). It also returns the accumulated options along the way. + * + * @param prefixes + * @param bool + * @param left + * @param right + * @return the pruned result + */ + public Result prune(final Map prefixes, final CQLBooleanNode bool, final CQLNode left, final CQLNode right) { + + if (isOption(prefixes, left) && isOption(prefixes, right)) { + return new Result(null, Stream.of(trimOption(prefixes, left, right), trimOption(prefixes, right, left)) + .filter(Objects::nonNull) + .map(i -> i.getOptions()) + .map(i -> i.stream()) + .flatMap(i -> i) + .collect(Collectors.toList())); + } + + Result res = anyNotNull(trimOption(prefixes, left, right), trimOption(prefixes, right, left)); + + if (res != null) + return res; + + final Result leftResult = prune(prefixes, left); + final Result rightResult = prune(prefixes, right); + + bool.left = leftResult.getNode(); + bool.right = rightResult.getNode(); + return new Result(clean(bool), Iterables.concat(leftResult.getOptions(), rightResult.getOptions())); + } + + public T anyNotNull(T a, T b) { + if (a != null) + return a; + return b; + } + + /** + * Trims an option from a boolean node if one if it's sides is an option term. + * + * Intended to be used once for each sides and then swap. + * + * @param prefixes + * @param a + * @param b + * @return the pruned result + */ + public Result trimOption(final Map prefixes, final CQLNode a, final CQLNode b) { + log.debug("trim option?" + prefixes + " a " + a.toCQL()); + if (isOption(prefixes, a)) { + log.debug("IS OPTION..."); + return trimOption(prefixes, prefixFromOption(a), getOption(a), b); + } + log.debug("IS NOT OPTION"); + return null; + } + + /** + * prune(prefixes, bool, left, right) uses this helper method to do the dirty job: + * + * we have to detect if a term node is a term option node. by checking the namespace uri associated with the term + * prefix according the the current namespace prefix scope (held in prefixes, which is passed down recursively by + * copy). + * + * @param prefixes + * @param ns + * @param o + * @param subtree + * @return the pruned result + */ + public Result trimOption(final Map prefixes, final String ns, final String o, final CQLNode subtree) { + log.debug("trimming " + prefixes + " ns " + ns + " o " + o); + + final String namespaceUri = prefixes.get(ns); + + if (!optionUri.equals(namespaceUri)) { + return null; + } + + final Result res = prune(prefixes, subtree); + return new Result(res.getNode(), Iterables.concat(Lists.newArrayList(o), res.getOptions())); + } + + /** + * Drop a boolean node (and, or etc) if one of the sides has been dropped. + * + * @param bool + * @return the pruned result + */ + private CQLNode clean(final CQLBooleanNode bool) { + if (bool.left == null) + return bool.right; + if (bool.right == null) + return bool.left; + return bool; + } + + ////////////////// helpers + + public String getOption(final CQLNode node) { + return indexFromOption(node) + "=" + termFromOption(node); + } + + private String indexFromOption(final CQLNode node) { + return ((CQLTermNode) node).getIndex().replaceAll("[a-z]*\\.(.+)", "$1"); + } + + private String termFromOption(final CQLNode node) { + return ((CQLTermNode) node).getTerm(); + } + + public String prefixFromOption(final String option) { + return option.replaceAll("([a-z]*)\\..+", "$1"); + } + + public String prefixFromOption(final CQLNode node) { + if (node instanceof CQLTermNode) + return prefixFromOption(((CQLTermNode) node).getIndex()); + + return null; + } + + public boolean isOption(final Map prefixes, final String option) { + return prefixes.containsKey(prefixFromOption(option)) && prefixes.get(prefixFromOption(option)).equals(getOptionUri()); + } + + public boolean isOption(final Map prefixes, final CQLNode node) { + if (node instanceof CQLTermNode) + return isOption(prefixes, ((CQLTermNode) node).getIndex()); + + return false; + } + + public String getOptionUri() { + return optionUri; + } + + public void setOptionUri(String optionUri) { + this.optionUri = optionUri; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/QueryLanguage.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/QueryLanguage.java new file mode 100644 index 0000000..afe8061 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/QueryLanguage.java @@ -0,0 +1,5 @@ +package eu.dnetlib.functionality.index.query; + +public enum QueryLanguage { + CQL, SOLR +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/QueryResponseFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/QueryResponseFactory.java new file mode 100644 index 0000000..3e984ab --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/QueryResponseFactory.java @@ -0,0 +1,57 @@ +package eu.dnetlib.functionality.index.query; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.functionality.index.client.IndexClientException; +import eu.dnetlib.functionality.index.utils.MetadataReference; +import eu.dnetlib.miscutils.functional.UnaryFunction; + +public abstract class QueryResponseFactory { + + /** + * tells to getBrowsingResults method if empty fields must be returned or not. + */ + protected boolean returnEmptyFields; + + /** + * force solr to return record ranking or not. + */ + protected boolean includeRanking; + + /** + * utility for highlighting. + */ + protected UnaryFunction highlightUtils; + + @Autowired + protected BrowseAliases browseAliases; + + public abstract QueryResponseParser getQueryResponseParser(final IndexQueryResponse queryRsp, final MetadataReference mdRef) throws IndexClientException; + + // /////////////////// setters and getters. + + @Required + public void setReturnEmptyFields(final boolean returnEmptyFields) { + this.returnEmptyFields = returnEmptyFields; + } + + public boolean isReturnEmptyFields() { + return returnEmptyFields; + } + + @Required + public void setHighlightUtils(final UnaryFunction highlightUtils) { + this.highlightUtils = highlightUtils; + } + + @Required + public void setIncludeRanking(final boolean includeRanking) { + this.includeRanking = includeRanking; + } + + public boolean isIncludeRanking() { + return includeRanking; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/QueryResponseParser.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/QueryResponseParser.java new file mode 100644 index 0000000..cf96496 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/QueryResponseParser.java @@ -0,0 +1,168 @@ +package eu.dnetlib.functionality.index.query; + +import java.util.Collection; +import java.util.List; + +import com.google.common.collect.BiMap; +import com.google.common.collect.Iterables; + +import eu.dnetlib.data.provision.index.rmi.BrowsingRow; +import eu.dnetlib.functionality.index.model.document.IndexDocument; +import eu.dnetlib.functionality.index.utils.IndexFieldUtility; +import eu.dnetlib.miscutils.functional.UnaryFunction; + +/** + * The Class QueryResponseParser. + */ +public abstract class QueryResponseParser { + + /** The highlight utils. */ + protected final UnaryFunction highlightUtils; + + /** The aliases. */ + protected final BiMap aliases; + + /** The return empty fields. */ + protected final boolean returnEmptyFields; + + /** The include ranking. */ + protected final boolean includeRanking; + + /** The wrapper rank. */ + protected final UnaryFunction wrapperRank = + doc -> addRanking(getSingleField(doc, IndexFieldUtility.RESULT), getSingleField(doc, IndexFieldUtility.SCORE_FIELD)); + + /** The wrapper no rank. */ + protected final UnaryFunction wrapperNoRank = doc -> wrap(getSingleField(doc, IndexFieldUtility.RESULT)); + + /** + * Gets the single field. + * + * @param doc + * the doc + * @param fieldName + * the field name + * @return the single field + */ + @SuppressWarnings("unchecked") + private String getSingleField(final IndexDocument doc, final String fieldName) { + Object value = doc.getFieldValue(fieldName); + if (value instanceof Collection) return Iterables.getOnlyElement((Iterable) value); + return String.valueOf(value); + } + + /** + * Instantiates a new query response parser. + * + * @param highlightUtils + * the highlight utils + * @param aliases + * the aliases + * @param returnEmptyFields + * the return empty fields + * @param includeRanking + * the include ranking + */ + public QueryResponseParser(final UnaryFunction highlightUtils, final BiMap aliases, final boolean returnEmptyFields, + final boolean includeRanking) { + + this.highlightUtils = highlightUtils; + this.aliases = aliases; + this.returnEmptyFields = returnEmptyFields; + this.includeRanking = includeRanking; + } + + /** + * Converts a String document to + * + * [document] . + * + * @param doc + * the doc + * @param score + * the score + * @return the string + */ + private String addRanking(final String doc, final String score) { + return new String("" + doc + ""); + } + + /** + * Wraps the given document as [document] . + * + * @param doc + * the doc + * @return the string + */ + private String wrap(final String doc) { + return new String("" + doc + ""); + } + + /** + * Gets the num found. + * + * @return the num found + */ + public abstract long getNumFound(); + + /** + * Gets the query time. + * + * @return the query time + */ + public abstract int getQueryTime(); + + /** + * Gets the elapsed time. + * + * @return the elapsed time + */ + public abstract long getElapsedTime(); + + /** + * Gets the status. + * + * @return the status + */ + public abstract String getStatus(); + + /** + * Gets the start. + * + * @return the start + */ + public abstract long getStart(); + + /** + * Gets the current size. + * + * @return the current number of documents. + */ + public abstract int getCurrentSize(); + + /** + * Gets the results. + * + * @return query results as a List + */ + public abstract List getResults(); + + /** + * method counts the number of facet fields resulting from the performed query. + * + * @return the number of browsing results + */ + public abstract Long getNumberOfBrowsingResults(); + + /** + * Gets the browsing results. + * + * @return the browsing results + */ + public abstract List getBrowsingResults(); + + public BiMap getAliases() { + return aliases; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexDocument.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexDocument.java new file mode 100644 index 0000000..c59240f --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexDocument.java @@ -0,0 +1,90 @@ +package eu.dnetlib.functionality.index.query; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import eu.dnetlib.functionality.index.model.Any.ValueType; +import eu.dnetlib.functionality.index.model.document.AbstractIndexDocument; +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.SolrInputField; + +// TODO: Auto-generated Javadoc + +/** + * The Class SolrIndexDocument an implementation of the index document for SOLR. + */ +public class SolrIndexDocument extends AbstractIndexDocument { + + /** + * Instantiates a new solr index document. + * + * @param schema + * the schema + * @param dsId + * the ds id + */ + public SolrIndexDocument(final Map schema, final String dsId) { + super(schema, dsId); + } + + /** + * Instantiates a new solr index document. + * + * @param schema + * the schema + * @param dsId + * the ds id + * @param solrDocument + * the solr document + */ + public SolrIndexDocument(final Map schema, final String dsId, final SolrInputDocument solrDocument) { + super(schema, dsId); + addFields(solrDocument); + } + + /** + * Adds the fields. + * + * @param solrDocument + * the solr document + */ + private void addFields(final SolrInputDocument solrDocument) { + for (String name : solrDocument.getFieldNames()) { + Collection fieldValues = solrDocument.getFieldValues(name); + if (fieldValues.size() > 1) { + addField(name, fieldValues); + } else if (fieldValues.size() == 1) { + addField(name, fieldValues.iterator().next()); + } + } + } + + /** + * Sets the content. + * + * @param solrDocument + * the new content + */ + public void setContent(final SolrInputDocument solrDocument) { + addFields(solrDocument); + } + + /** + * Gets the solr document. + * + * @return the solr document + */ + public SolrInputDocument getSolrDocument() { + + Map data = new HashMap<>(); + for (String key : fields.keySet()) { + SolrInputField solrField = new SolrInputField(key); + for (Object o : fields.get(key)) { + solrField.addValue(o); + } + data.put(key, solrField); + } + return new SolrInputDocument(data); + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQuery.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQuery.java new file mode 100644 index 0000000..b9e0f1c --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQuery.java @@ -0,0 +1,125 @@ +package eu.dnetlib.functionality.index.query; + +import java.util.List; +import java.util.Map; + +import eu.dnetlib.functionality.cql.lucene.QueryOptions; +import eu.dnetlib.functionality.cql.lucene.TranslatedQuery; +import eu.dnetlib.functionality.index.utils.IndexFieldUtility; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; + +/** + * The Class SolrIndexQuery. + * + * @author claudio, sandro + */ +public class SolrIndexQuery extends SolrQuery implements IndexQuery { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(SolrIndexQuery.class); + + /** + * Instantiates a new solr index query. + * + * @param query + * the query + * @param options + * the options + */ + public SolrIndexQuery(final TranslatedQuery query, final Map> options) { + this(query.asLucene(), options); + + setCqlParams(query.getOptions()); + + log.debug("internal solr query: " + this.toString()); + } + + /** + * Instantiates a new solr index query. + * + * @param query + * the query + * @param options + * the options + */ + public SolrIndexQuery(final String query, final Map> options) { + this(query); + + // TODO verify that the input options belongs to solr + super.add(getQueryParams(options)); + } + + /** + * Instantiates a new solr index query. + * + * @param query + * the query + */ + public SolrIndexQuery(final String query) { + super(query); + } + + @Override + public IndexQuery setQueryOffset(final int offset) { + super.setStart(offset); + return this; + } + + @Override + public IndexQuery setQueryLimit(final int limit) { + super.setRows(limit); + return this; + } + + /** + * Checks if is all. + * + * @param dsIds + * the ds id + * @return true, if is all + */ + protected boolean isAll(final List dsIds) { + return (dsIds != null) && (!dsIds.isEmpty()) && (dsIds.size() == 1) && dsIds.get(0).equalsIgnoreCase(IndexFieldUtility.INDEX_DSID_ALL); + } + + /** + * Convert our option map to a solr option parameter map. + * + * @param options + * input paramter map. + * @return solr option parameter map. + */ + private SolrParams getQueryParams(final Map> options) { + ModifiableSolrParams params = new ModifiableSolrParams(); + String[] typeTag = new String[] {}; + + for (Map.Entry> entry : options.entrySet()) { + params.add(entry.getKey(), entry.getValue().toArray(typeTag)); + } + return params; + } + + /** + * Sets the query options. + * + * @param options + * the options. + */ + private void setCqlParams(final QueryOptions options) { + if (options != null) { + if (options.getSort() != null) { + super.addSort(options.getSort().getField(), SolrQuery.ORDER.valueOf(options.getSort().getMode().name())); + } + } + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQueryFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQueryFactory.java new file mode 100644 index 0000000..8db866c --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQueryFactory.java @@ -0,0 +1,72 @@ +package eu.dnetlib.functionality.index.query; + +import java.util.Arrays; + +import eu.dnetlib.functionality.cql.lucene.TranslatedQuery; +import eu.dnetlib.functionality.index.client.IndexClient; +import eu.dnetlib.functionality.index.query.Pruner.Result; +import eu.dnetlib.functionality.index.solr.utils.HighlightUtils; +import eu.dnetlib.functionality.index.utils.IndexFieldUtility; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A factory for creating SolrIndexQuery objects. + */ +public class SolrIndexQueryFactory extends IndexQueryFactory { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(SolrIndexQueryFactory.class); + + /** The Property name SERVICE_HIGHLIGHT_ENABLE. */ + private static final String SERVICE_HIGHLIGHT_ENABLE = "service.index.solr.highlight.enable"; + + /* + * (non-Javadoc) + * + * @see eu.dnetlib.functionality.index.query.IndexQueryFactory#newInstance(eu.dnetlib.functionality.index.cql.TranslatedQuery, + * eu.dnetlib.functionality.index.query.Pruner.Result, eu.dnetlib.functionality.index.query.QueryLanguage) + */ + @Override + protected IndexQuery newInstance(final TranslatedQuery cql, final Result res, final QueryLanguage lang) { + + switch (lang) { + case CQL: + return new SolrIndexQuery(cql, res.getOptionMap()); + case SOLR: + return new SolrIndexQuery(res.getNode().toCQL(), res.getOptionMap()); + default: + throw new IllegalArgumentException("invalid query language: " + lang); + } + } + + /* + * (non-Javadoc) + * + * @see eu.dnetlib.functionality.index.query.IndexQueryFactory#setQueryOptions(eu.dnetlib.functionality.index.query.IndexQuery, + * eu.dnetlib.functionality.index.IndexServerDAO) + */ + @Override + protected IndexQuery setQueryOptions(final IndexQuery indexQuery, final IndexClient client) { + + final SolrIndexQuery solrQuery = (SolrIndexQuery) indexQuery; + + boolean isHighlightEnabled = Boolean.parseBoolean(client.getServiceProperties().get(SERVICE_HIGHLIGHT_ENABLE)); + if (solrQuery.getHighlight() & isHighlightEnabled) { + solrQuery.setHighlightFragsize(0).setHighlightSnippets(1).setHighlightSimplePre(HighlightUtils.DEFAULT_HL_PRE) + .setHighlightSimplePost(HighlightUtils.DEFAULT_HL_POST).addHighlightField(IndexFieldUtility.RESULT) + .addField(IndexFieldUtility.INDEX_RECORD_ID); + } + + solrQuery.addField(IndexFieldUtility.RESULT); + if (solrQuery.getFacetFields() != null) { + log.debug("getFacetFields() " + Arrays.asList(solrQuery.getFacetFields())); + solrQuery.setFacetMinCount(1); + } + + return solrQuery; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQueryResponse.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQueryResponse.java new file mode 100644 index 0000000..0f0e69b --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQueryResponse.java @@ -0,0 +1,22 @@ +package eu.dnetlib.functionality.index.query; + +import eu.dnetlib.functionality.index.query.IndexQueryResponse; +import org.apache.solr.client.solrj.response.QueryResponse; + +/** + * The Class SolrIndexQueryResponse. + */ +public class SolrIndexQueryResponse implements IndexQueryResponse { + + private QueryResponse solrQueryResponse; + + public SolrIndexQueryResponse(final QueryResponse solrQueryResponse) { + this.solrQueryResponse = solrQueryResponse; + } + + @Override + public QueryResponse getContextualQueryResponse() { + return solrQueryResponse; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQueryResponseFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQueryResponseFactory.java new file mode 100644 index 0000000..3c149b5 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrIndexQueryResponseFactory.java @@ -0,0 +1,28 @@ +package eu.dnetlib.functionality.index.query; + +import eu.dnetlib.functionality.index.client.IndexClientException; +import eu.dnetlib.functionality.index.utils.MetadataReference; +import org.apache.solr.client.solrj.response.QueryResponse; + +/** + * The Class SolrIndexQueryResponseFactory. + */ +public class SolrIndexQueryResponseFactory extends QueryResponseFactory { + + /** + * {@inheritDoc} + * + * @throws IndexClientException + * + * @see QueryResponseFactory#getQueryResponseParser(IndexQueryResponse, + * MetadataReference) + */ + @Override + public QueryResponseParser getQueryResponseParser(final IndexQueryResponse queryRsp, final MetadataReference mdRef) + throws IndexClientException { + + QueryResponse response = queryRsp.getContextualQueryResponse(); + return new SolrResponseParser(highlightUtils, browseAliases.get(mdRef), returnEmptyFields, includeRanking, response); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrResponseParser.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrResponseParser.java new file mode 100644 index 0000000..e13e4a2 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/SolrResponseParser.java @@ -0,0 +1,360 @@ +package eu.dnetlib.functionality.index.query; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +import com.google.common.base.Predicate; +import com.google.common.collect.BiMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import eu.dnetlib.data.provision.index.rmi.BrowsingRow; +import eu.dnetlib.data.provision.index.rmi.GroupResult; +import eu.dnetlib.functionality.index.utils.IndexFieldUtility; +import eu.dnetlib.miscutils.functional.UnaryFunction; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.solr.client.solrj.response.FacetField; +import org.apache.solr.client.solrj.response.FacetField.Count; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; + +import static eu.dnetlib.miscutils.collections.MappedCollection.listMap; + +/** + * The Class SolrResponseParser. + */ +public class SolrResponseParser extends QueryResponseParser { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(SolrResponseParser.class); + + /** + * Lower level response. + */ + private QueryResponse queryRsp = null; + + /** The wrapper rank. */ + protected final UnaryFunction wrapperRank = + doc -> addRanking( + getSingleField(doc, IndexFieldUtility.RESULT), + getSingleField(doc, IndexFieldUtility.SCORE_FIELD)); + + /** The wrapper no rank. */ + protected final UnaryFunction wrapperNoRank = + doc -> wrap(getSingleField(doc, IndexFieldUtility.RESULT)); + + /** + * The Constructor. + * + * @param highlightUtils + * the highlight utils + * @param aliases + * the aliases + * @param returnEmptyFields + * the return empty fields + * @param includeRanking + * the include ranking + * @param response + * the response + */ + public SolrResponseParser(final UnaryFunction highlightUtils, final BiMap aliases, final boolean returnEmptyFields, + final boolean includeRanking, final QueryResponse response) { + super(highlightUtils, aliases, returnEmptyFields, includeRanking); + this.queryRsp = response; + } + + /** + * {@inheritDoc} + * + * @see QueryResponseParser#getNumFound() + */ + @Override + public long getNumFound() { + + return this.queryRsp.getResults().getNumFound(); + } + + /** + * {@inheritDoc} + * + * @see QueryResponseParser#getQueryTime() + */ + @Override + public int getQueryTime() { + return queryRsp.getQTime(); + } + + /** + * {@inheritDoc} + * + * @see QueryResponseParser#getElapsedTime() + */ + @Override + public long getElapsedTime() { + return queryRsp.getElapsedTime(); + } + + /** + * {@inheritDoc} + * + * @see QueryResponseParser#getStatus() + */ + @Override + public String getStatus() { + return String.valueOf(queryRsp.getStatus()); + } + + /** + * {@inheritDoc} + * + * @see QueryResponseParser#getCurrentSize() + */ + @Override + public int getCurrentSize() { + return queryRsp.getResults().size(); + } + + /** + * Gets the query response. + * + * @return the query response + */ + public QueryResponse getQueryResponse() { + return queryRsp; + } + + /** + * {@inheritDoc} + * + * @see QueryResponseParser#getResults() + */ + @Override + public List getResults() { + return asRankedList(queryRsp.getResults()); + } + + /** + * {@inheritDoc} + * + * @see QueryResponseParser#getNumberOfBrowsingResults() + */ + @Override + public Long getNumberOfBrowsingResults() { + List ffList = queryRsp.getFacetFields(); + Long maxCount = 0L; + + if (ffList != null) { + for (FacetField ff : ffList) { + if (ff != null) { + Long countFacets = countFacets(ff.getValues()); + if (countFacets > maxCount) { + maxCount = countFacets; + } + } + } + } + return maxCount; + } + + /** + * {@inheritDoc} + * + * @see QueryResponseParser#getBrowsingResults() + */ + @Override + public List getBrowsingResults() { + List bresList = Lists.newArrayList(); + List facets = Lists.newArrayList(); + + final List ffList = queryRsp.getFacetFields(); + + Long numberOfBrowsingResults = getNumberOfBrowsingResults(); + for (int i = 0; (ffList != null) && (i < numberOfBrowsingResults); i++) { + for (FacetField ff : ffList) { + + String name = null; + if (aliases != null) { + name = aliases.inverse().get(ff.getName()); + } + + // fix #1456 + if (name == null) { + name = ff.getName(); + } + + final Count facet = getFacet(ff, i); + + if ((facet != null) && (facet.getCount() > 0)) { + + final String value = facet.getName(); + final int count = (int) facet.getCount(); + + if (returnEmptyFields || !value.isEmpty()) { + facets.add(new GroupResult(name, value, count)); + } + } + } + + if (facets.size() > 0) { + bresList.add(new BrowsingRow(Lists.newArrayList(facets))); + facets.clear(); + } + } + if (log.isDebugEnabled()) { + log.debug("BrowsingResult size: " + bresList.size()); + } + return bresList; + } + + // /////////////// helpers + + /** + * Gets the facet. + * + * @param ff + * the ff + * @param pos + * the pos + * @return the facet + */ + private Count getFacet(final FacetField ff, final int pos) { + + if ((ff.getValues() == null) || (pos >= ff.getValues().size())) return null; + return ff.getValues().get(pos); + } + + /** + * Given SolrDocumentList, return a List of Strings, representing it. + * + * @param documentList + * the document list + * @return the list< string> + */ + private List asRankedList(final SolrDocumentList documentList) { + + UnaryFunction wrapper = includeRanking ? wrapperRank : wrapperNoRank; + + if (queryRsp.getHighlighting() != null) return listMap(listMap(documentList, doc -> { + + String score = getSingleField(doc, IndexFieldUtility.SCORE_FIELD); + + String hl = getHighlighting(getSingleField(doc, IndexFieldUtility.INDEX_RECORD_ID)); + String res = hl != null ? hl : getSingleField(doc, IndexFieldUtility.RESULT); + + return includeRanking ? addRanking(res, score) : wrap(res); + }), highlightUtils); + + return listMap(documentList, wrapper); + } + + /** + * Converts a String document to + * + * [document] . + * + * @param doc + * the doc + * @param score + * the score + * @return the string + */ + private String addRanking(final String doc, final String score) { + return new String("" + doc + ""); + } + + /** + * Wraps the given document as [document] . + * + * @param doc + * the doc + * @return the string + */ + private String wrap(final String doc) { + return new String("" + doc + ""); + } + + /** + * Gets the single field. + * + * @param doc + * the doc + * @param fieldName + * the field name + * @return the single field + */ + @SuppressWarnings("unchecked") + protected String getSingleField(final SolrDocument doc, final String fieldName) { + Object value = doc.getFieldValue(fieldName); + if (value instanceof Collection) return Iterables.getOnlyElement((Iterable) value); + return String.valueOf(value); + } + + private byte[] base64Decode(final String s) { + return org.apache.solr.common.util.Base64.base64ToByteArray(s); + } + + private String unzip(final byte[] b) { + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(b)) { + try (GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream)) { + return new String(IOUtils.toByteArray(gzipInputStream)); + } + } catch(IOException e) { + throw new RuntimeException("Failed to unzip content", e); + } + } + + /** + * Gets the highlighting. + * + * @param docId + * the doc id + * @return the highlighting + */ + private String getHighlighting(final String docId) { + final Map> highlight = queryRsp.getHighlighting().get(docId); + + String result = new String(); + if ((highlight != null) && (highlight.get(IndexFieldUtility.RESULT) != null)) { + for (String s : highlight.get(IndexFieldUtility.RESULT)) { + result = result.concat(s); + } + return result; + } + return null; + } + + /** + * helper method. + * + * @param facets + * the list of facets to analyze + * @return the number of non-empty facets in the list whose count is greater than zero + */ + private Long countFacets(final List facets) { + + if (facets == null) return 0L; + + return (long) Iterables.size(Iterables.filter(facets, new Predicate() { + + @Override + public boolean apply(final Count c) { + return (c != null) && (c.getName() != null) && !c.getName().isEmpty() && (c.getCount() > 0); + } + })); + } + + @Override + public long getStart() { + // TODO Auto-generated method stub + return queryRsp.getResults().getStart(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/Weights.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/Weights.java new file mode 100644 index 0000000..840908b --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/query/Weights.java @@ -0,0 +1,61 @@ +package eu.dnetlib.functionality.index.query; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import eu.dnetlib.functionality.index.client.IndexClientException; +import eu.dnetlib.functionality.index.utils.MDFormatReader; +import eu.dnetlib.functionality.index.utils.MetadataReference; +import eu.dnetlib.functionality.index.utils.ServiceTools; + +public class Weights extends HashMap> { + + private static final long serialVersionUID = -3517914310574484765L; + + private static final Log log = LogFactory.getLog(Weights.class); // NOPMD by marko on 11/24/08 5:02 PM + + @Autowired + private ServiceTools serviceTools; + + @Autowired + private MDFormatReader mdFormatReader; + + public Weights() { + super(); + } + + public void initialize() throws IndexClientException { + log.info("initializing weights"); + + for (MetadataReference mdRef : serviceTools.listMDRefs()) { + put(mdRef, mdFormatReader.getAttributeMap(mdRef, "weight")); + } + log.info("weights initialization completed"); + + } + + @Override + public Map put(final MetadataReference mdRef, final Map w) { + log.info("[" + mdRef + "]" + " adding weights: " + w); + return super.put(mdRef, w); + } + + @Override + public Map get(final Object mdRef) { + Map map = super.get(mdRef); + return map != null ? map : initAndGet(mdRef); + } + + private Map initAndGet(final Object mdRef) { + try { + initialize(); + } catch (IndexClientException e) { + throw new RuntimeException(e); + } + return super.get(mdRef); + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/cql/SimpleDateValueTransformer.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/cql/SimpleDateValueTransformer.java new file mode 100644 index 0000000..d768475 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/cql/SimpleDateValueTransformer.java @@ -0,0 +1,19 @@ +package eu.dnetlib.functionality.index.solr.cql; + +import eu.dnetlib.miscutils.functional.UnaryFunction; + +/** + * Simply and not very roboust normalizer for solr dates. Basically it handles well yyyy-mm-dd and + * yyyy-mm-ddThh:mm:ssZ + * + * @author marko + * + */ +public class SimpleDateValueTransformer implements UnaryFunction { + @Override + public String evaluate(final String value) { + if (!value.endsWith("Z")) + return value + "T00:00:00Z"; + return value; + } +} \ No newline at end of file diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/cql/SolrTypeBasedCqlValueTransformerMap.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/cql/SolrTypeBasedCqlValueTransformerMap.java new file mode 100644 index 0000000..101f92b --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/cql/SolrTypeBasedCqlValueTransformerMap.java @@ -0,0 +1,68 @@ +package eu.dnetlib.functionality.index.solr.cql; + +import java.util.Map; + +import eu.dnetlib.functionality.cql.CqlValueTransformerMap; +import eu.dnetlib.functionality.index.model.Any.ValueType; +import eu.dnetlib.miscutils.functional.IdentityFunction; +import eu.dnetlib.miscutils.functional.UnaryFunction; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.solr.common.SolrException; + +/** + * This class maps the fields in the given index schema with a transformation rule. + * + * @author marko + * + */ +public class SolrTypeBasedCqlValueTransformerMap implements CqlValueTransformerMap { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(SolrTypeBasedCqlValueTransformerMap.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * Index schema. + */ + private final Map schema; + + /** + * Map of functions. + */ + private final Map> transformerMap; + + /** + * Create value transformer map bound to a specific schema + * @param schema + * @param transformerMap + */ + public SolrTypeBasedCqlValueTransformerMap(final Map schema, final Map> transformerMap) { + this.schema = schema; + this.transformerMap = transformerMap; + } + + /** + * {@inheritDoc} + * + * @see CqlValueTransformerMap#transformerFor(String) + */ + @Override + public UnaryFunction transformerFor(final String fieldName) { + try { + final ValueType field = schema.get(fieldName); + + if (field != null) { + UnaryFunction res = transformerMap.get(field.name()); + if (res != null) { + return res; + } + } + } catch (SolrException e) { + log.debug("cannot find field", e); + } + return new IdentityFunction(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/cql/SolrTypeBasedCqlValueTransformerMapFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/cql/SolrTypeBasedCqlValueTransformerMapFactory.java new file mode 100644 index 0000000..1ed67c5 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/cql/SolrTypeBasedCqlValueTransformerMapFactory.java @@ -0,0 +1,41 @@ +package eu.dnetlib.functionality.index.solr.cql; + +import java.util.Map; + +import eu.dnetlib.functionality.index.model.Any.ValueType; +import eu.dnetlib.miscutils.functional.UnaryFunction; +import org.springframework.beans.factory.annotation.Required; + +/** + * Factory for the SolrTypeBasedCqlValueTransformerMap class objects + * + * @author claudio + * + */ +public class SolrTypeBasedCqlValueTransformerMapFactory { + + /** + * Map of functions, injected via spring. + */ + private Map> transformerMap; + + /** + * Method returns a new instance of SolrTypeBasedCqlValueTransformerMap. + * + * @param schema + * @return the SolrTypeBasedCqlValueTransformerMap + */ + public SolrTypeBasedCqlValueTransformerMap getIt(final Map schema) { + return new SolrTypeBasedCqlValueTransformerMap(schema, getTransformerMap()); + } + + @Required + public void setTransformerMap(Map> transformerMap) { + this.transformerMap = transformerMap; + } + + public Map> getTransformerMap() { + return transformerMap; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/feed/InputDocumentFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/feed/InputDocumentFactory.java new file mode 100644 index 0000000..a3f79f0 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/feed/InputDocumentFactory.java @@ -0,0 +1,72 @@ +package eu.dnetlib.functionality.index.solr.feed; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import javax.xml.stream.XMLStreamException; + +import org.apache.solr.common.SolrInputDocument; +import org.dom4j.DocumentException; + +/** + * + * @author claudio + * + */ +public abstract class InputDocumentFactory { + + public static final String INDEX_FIELD_PREFIX = "__"; + + public static final String DS_VERSION = INDEX_FIELD_PREFIX + "dsversion"; + + public static final String DS_ID = INDEX_FIELD_PREFIX + "dsid"; + + public static final String RESULT = "result"; + + public static final String INDEX_RESULT = INDEX_FIELD_PREFIX + RESULT; + + public static final String INDEX_RECORD_ID = INDEX_FIELD_PREFIX + "indexrecordidentifier"; + + private static final String outFormat = new String("yyyy-MM-dd'T'hh:mm:ss'Z'"); + + private final static List dateFormats = Arrays.asList("yyyy-MM-dd'T'hh:mm:ss", "yyyy-MM-dd", "dd-MM-yyyy", "dd/MM/yyyy", "yyyy"); + + public abstract SolrInputDocument parseDocument(final String version, + final String inputDocument, + final String dsId, + final String resultName) throws XMLStreamException; + + public abstract SolrInputDocument parseDocument(final String version, + final String inputDocument, + final String dsId, + final String resultName, + final ResultTransformer resultTransformer) throws XMLStreamException; + + /** + * method return a solr-compatible string representation of a date + * + * @param date + * @return the parsed date + * @throws DocumentException + * @throws ParseException + */ + public static String getParsedDateField(final String date) { + return new SimpleDateFormat(outFormat).format(tryParse(date)); + } + + public static Date tryParse(final String date) { + for (String formatString : dateFormats) { + try { + return new SimpleDateFormat(formatString).parse(date); + } catch (ParseException e) {} + } + throw new IllegalStateException("unable to parse date: " + date); + } + + public String parseDate(final String date) { + return getParsedDateField(date); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/feed/ResultTransformer.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/feed/ResultTransformer.java new file mode 100644 index 0000000..1fd95d2 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/feed/ResultTransformer.java @@ -0,0 +1,26 @@ +package eu.dnetlib.functionality.index.solr.feed; + +import com.google.common.base.Function; + +/** + * Created by claudio on 17/11/15. + */ +public abstract class ResultTransformer implements Function { + + public enum Mode {compress, empty, xslt, base64} + + protected Mode mode; + + public ResultTransformer(final Mode mode) { + this.mode = mode; + } + + public Mode getMode() { + return mode; + } + + public void setMode(final Mode mode) { + this.mode = mode; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/feed/StreamingInputDocumentFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/feed/StreamingInputDocumentFactory.java new file mode 100644 index 0000000..78039a0 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/feed/StreamingInputDocumentFactory.java @@ -0,0 +1,258 @@ +package eu.dnetlib.functionality.index.solr.feed; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import javax.xml.stream.*; +import javax.xml.stream.events.Namespace; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import com.google.common.collect.Lists; +import eu.dnetlib.functionality.index.solr.feed.ResultTransformer.Mode; +import org.apache.solr.common.SolrInputDocument; + +/** + * Optimized version of the document parser, drop in replacement of InputDocumentFactory. + * + *

    + * Faster because: + *

    + *
      + *
    • Doesn't create a DOM for the full document
    • + *
    • Doesn't execute xpaths agains the DOM
    • + *
    • Quickly serialize the 'result' element directly in a string.
    • + *
    • Uses less memory: less pressure on GC and allows more threads to process this in parallel
    • + *
    + * + *

    + * This class is fully reentrant and can be invoked in parallel. + *

    + * + * @author marko + * + */ +public class StreamingInputDocumentFactory extends InputDocumentFactory { + + protected static final String DEFAULTDNETRESULT = "dnetResult"; + + protected static final String TARGETFIELDS = "targetFields"; + + protected static final String INDEX_RECORD_ID_ELEMENT = "indexRecordIdentifier"; + + protected static final String ROOT_ELEMENT = "indexRecord"; + + protected static final int MAX_FIELD_LENGTH = 25000; + + protected ThreadLocal inputFactory = new ThreadLocal() { + + @Override + protected XMLInputFactory initialValue() { + return XMLInputFactory.newInstance(); + } + }; + + protected ThreadLocal outputFactory = new ThreadLocal() { + + @Override + protected XMLOutputFactory initialValue() { + return XMLOutputFactory.newInstance(); + } + }; + + protected ThreadLocal eventFactory = new ThreadLocal() { + + @Override + protected XMLEventFactory initialValue() { + return XMLEventFactory.newInstance(); + } + }; + + @Override + public SolrInputDocument parseDocument(final String version, final String inputDocument, final String dsId, final String resultName) + throws XMLStreamException { + return parseDocument(version, inputDocument, dsId, resultName, null); + } + + @Override + public SolrInputDocument parseDocument(final String version, + final String inputDocument, + final String dsId, + final String resultName, + final ResultTransformer resultTransformer) { + + final StringWriter results = new StringWriter(); + final List nsList = Lists.newLinkedList(); + try { + + XMLEventReader parser = inputFactory.get().createXMLEventReader(new StringReader(inputDocument)); + + final SolrInputDocument indexDocument = new SolrInputDocument(new HashMap<>()); + + while (parser.hasNext()) { + final XMLEvent event = parser.nextEvent(); + if ((event != null) && event.isStartElement()) { + final String localName = event.asStartElement().getName().getLocalPart(); + + if (ROOT_ELEMENT.equals(localName)) { + nsList.addAll(getNamespaces(event)); + } else if (INDEX_RECORD_ID_ELEMENT.equals(localName)) { + final XMLEvent text = parser.nextEvent(); + String recordId = getText(text); + indexDocument.addField(INDEX_RECORD_ID, recordId); + } else if (TARGETFIELDS.equals(localName)) { + parseTargetFields(indexDocument, parser); + } else if (resultName.equals(localName)) { + if (resultTransformer == null || !(Mode.empty.equals(resultTransformer.getMode()))) { + copyResult(indexDocument, results, parser, nsList, resultName, resultTransformer); + } + } + } + } + + if (version != null) { + indexDocument.addField(DS_VERSION, version); + } + + if (dsId != null) { + indexDocument.addField(DS_ID, dsId); + } + + if (!indexDocument.containsKey(INDEX_RECORD_ID)) { + indexDocument.clear(); + System.err.println("missing indexrecord id:\n" + inputDocument); + } + + return indexDocument; + } catch (XMLStreamException e) { + return new SolrInputDocument(); + } + } + + private List getNamespaces(final XMLEvent event) { + final List res = Lists.newLinkedList(); + @SuppressWarnings("unchecked") + Iterator nsIter = event.asStartElement().getNamespaces(); + while (nsIter.hasNext()) { + Namespace ns = nsIter.next(); + res.add(ns); + } + return res; + } + + /** + * Parse the targetFields block and add fields to the solr document. + * + * @param indexDocument + * @param parser + * @throws XMLStreamException + */ + protected void parseTargetFields(final SolrInputDocument indexDocument, final XMLEventReader parser) throws XMLStreamException { + + boolean hasFields = false; + + while (parser.hasNext()) { + final XMLEvent targetEvent = parser.nextEvent(); + if (targetEvent.isEndElement() && targetEvent.asEndElement().getName().getLocalPart().equals(TARGETFIELDS)) { + break; + } + + if (targetEvent.isStartElement()) { + final String fieldName = targetEvent.asStartElement().getName().getLocalPart(); + final XMLEvent text = parser.nextEvent(); + + String data = getText(text); + + addField(indexDocument, fieldName, data); + hasFields = true; + } + } + + if (!hasFields) { + indexDocument.clear(); + } + } + + /** + * Copy the /indexRecord/result element and children, preserving namespace declarations etc. + * + * @param indexDocument + * @param results + * @param parser + * @param nsList + * @throws XMLStreamException + */ + protected void copyResult(final SolrInputDocument indexDocument, + final StringWriter results, + final XMLEventReader parser, + final List nsList, + final String dnetResult, + final ResultTransformer resultTransformer) throws XMLStreamException { + final XMLEventWriter writer = outputFactory.get().createXMLEventWriter(results); + + for (Namespace ns : nsList) { + eventFactory.get().createNamespace(ns.getPrefix(), ns.getNamespaceURI()); + } + + StartElement newRecord = eventFactory.get().createStartElement("", null, RESULT, null, nsList.iterator()); + + // new root record + writer.add(newRecord); + + // copy the rest as it is + while (parser.hasNext()) { + final XMLEvent resultEvent = parser.nextEvent(); + + // TODO: replace with depth tracking instead of close tag tracking. + if (resultEvent.isEndElement() && resultEvent.asEndElement().getName().getLocalPart().equals(dnetResult)) { + writer.add(eventFactory.get().createEndElement("", null, RESULT)); + break; + } + + writer.add(resultEvent); + } + writer.close(); + + if (resultTransformer != null) { + indexDocument.addField(INDEX_RESULT, resultTransformer.apply(results.toString())); + } else { + indexDocument.addField(INDEX_RESULT, results.toString()); + } + } + + /** + * Helper used to add a field to a solr doc. It avoids to add empy fields + * + * @param indexDocument + * @param field + * @param value + */ + private final void addField(final SolrInputDocument indexDocument, final String field, final String value) { + String cleaned = value.trim(); + if (!cleaned.isEmpty()) { + // log.info("\n\n adding field " + field.toLowerCase() + " value: " + cleaned + "\n"); + indexDocument.addField(field.toLowerCase(), cleaned); + } + } + + /** + * Helper used to get the string from a text element. + * + * @param text + * @return the + */ + protected final String getText(final XMLEvent text) { + if (text.isEndElement()) // log.warn("skipping because isEndOfElement " + text.asEndElement().getName().getLocalPart()); + return ""; + + final String data = text.asCharacters().getData(); + if (data != null && data.length() > MAX_FIELD_LENGTH) { + return data.substring(0, MAX_FIELD_LENGTH); + } + + return data; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/utils/HighlightUtils.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/utils/HighlightUtils.java new file mode 100644 index 0000000..aa07fcb --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/solr/utils/HighlightUtils.java @@ -0,0 +1,41 @@ +package eu.dnetlib.functionality.index.solr.utils; + +import eu.dnetlib.miscutils.functional.UnaryFunction; +import org.apache.oro.text.perl.Perl5Util; + +public class HighlightUtils implements UnaryFunction { + + public final static String DEFAULT_HL_PRE = "[hl]"; + + public final static String DEFAULT_HL_POST = "[/hl]"; + + private static String CLEAN_HEADER = "s#\\[/?hl\\]##gm"; + private static String CLEAN_REGEX_OPEN = "<([^>]*)\\[hl\\]([^>]*)>"; + private static String CLEAN_REGEX_CLOSE = "<([^>]*)\\[\\/hl\\]([^>]*)>"; + + // private static String CLEAN_REGEX_OPEN = "s#<([^>]*)\\[hl\\]([^>]*)>#<$1$2>#gm"; + // private static String CLEAN_REGEX_CLOSE = "s#<([^>]*)\\[\\/hl\\]([^>]*)>#<$1$2>#gm"; + + private Perl5Util p5util = new Perl5Util(); + + @Override + public String evaluate(final String doc) { + String[] chunk = doc.split(""); + String string = cleanHeader(chunk[0]) + "" + cleanBody(chunk[1]); + return string; + } + + private String cleanHeader(final String header) { + return p5util.substitute(CLEAN_HEADER, header); + } + + // TODO: implement a faster way to do this + private String cleanBody(final String body) { + String res = body.replaceAll(CLEAN_REGEX_OPEN, "<$1$2>").replaceAll(CLEAN_REGEX_CLOSE, "<$1$2>"); + + if (res.equals(body)) return res; + + return cleanBody(res); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/IndexFieldUtility.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/IndexFieldUtility.java new file mode 100644 index 0000000..d657118 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/IndexFieldUtility.java @@ -0,0 +1,41 @@ +package eu.dnetlib.functionality.index.utils; + +/** + * IndexMap keeps track of IndexDataStructure-to-physical index mappings and vice-versa. + * + * @author claudio + * + */ +public class IndexFieldUtility { + + public static final String INDEX_FIELD_PREFIX = "__"; + + public static final String INDEX_DSID_ALL = "ALL"; + + public static final String SCORE_FIELD = "score"; + + public static final String DS_ID = INDEX_FIELD_PREFIX + "dsid"; + + public static final String DS_VERSION = INDEX_FIELD_PREFIX + "dsversion"; + + public static final String RESULT = INDEX_FIELD_PREFIX + "result"; + + public static final String ALL_FIELDS = INDEX_FIELD_PREFIX + "all"; + + public static final String DELETE_DOCUMENT = INDEX_FIELD_PREFIX + "deleted"; + + public static final String INDEX_RECORD_ID = INDEX_FIELD_PREFIX + "indexrecordidentifier"; + + public static final String FULLTEXT_ID = INDEX_FIELD_PREFIX + "fulltext"; + + public static final String INDEXNAME_PARAM = INDEX_FIELD_PREFIX + "indexName"; + + public static final String queryAll = "*:*"; + + public static final String FIELD_NAME = "name"; + + public static final String FIELD_BROWSING_ALIAS_FOR = "browsingAliasFor"; + + public static final String XPATH_BROWSING_ALIAS_FOR = "//FIELD[@" + FIELD_BROWSING_ALIAS_FOR + "]"; + +} \ No newline at end of file diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/MDFormatReader.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/MDFormatReader.java new file mode 100644 index 0000000..2328656 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/MDFormatReader.java @@ -0,0 +1,103 @@ +package eu.dnetlib.functionality.index.utils; + +import java.io.StringReader; +import java.util.List; +import java.util.Map; + +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.io.SAXReader; +import org.springframework.beans.factory.annotation.Autowired; +import org.xml.sax.InputSource; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import com.mycila.xmltool.CallBack; +import com.mycila.xmltool.XMLDoc; +import com.mycila.xmltool.XMLTag; + +import eu.dnetlib.data.provision.index.rmi.IndexServiceException; +import eu.dnetlib.functionality.index.client.IndexClientException; + +public class MDFormatReader { + + static class XmlUtils { + + /** + * helper method, parses a list of fields. + * + * @param fields + * the given fields + * @return the parsed fields + * @throws IndexServiceException + * if cannot parse the fields + */ + public static Document parse(final String xml) { + try { + return new SAXReader().read(new StringReader(xml)); + } catch (DocumentException e) { + throw new IllegalArgumentException("cannot parse: " + xml); + } + } + + public static InputSource asInputSource(final String input) throws DocumentException { + return new InputSource(new StringReader(parse(input).asXML())); + } + } + + @Autowired + private ServiceTools serviceTools; + + @Autowired + private MetadataReferenceFactory mdFactory; + + public List listMDRefs() throws IndexClientException { + return serviceTools.listMDRefs(); + } + + public Document getFields(final MetadataReference mdRef) { + String fields = serviceTools.getIndexFields(mdRef); + return (fields != null) && !fields.isEmpty() ? XmlUtils.parse(fields) : null; + } + + public Map getAttributeMap(final MetadataReference mdRef, final String attribute) throws IndexClientException { + return getAttributeTable(mdRef, attribute).column(attribute); + } + + public Table getAttributeTable(final MetadataReference mdRef, final String... attributeList) throws IndexClientException { + + final String fields = serviceTools.getIndexFields(mdRef); + if (fields.isEmpty()) throw new IndexClientException("No result getting index layout informations"); + final Table t = HashBasedTable.create(); + XMLDoc.from(serviceTools.getIndexFields(mdRef), false).forEach("//FIELD", new CallBack() { + + @Override + public void execute(final XMLTag field) { + + for (String attribute : attributeList) { + String value = null; + + if ("xpath".equals(attribute)) { + if (!(field.hasAttribute("xpath") || field.hasAttribute("value"))) return; + + value = field.hasAttribute("xpath") ? field.getAttribute("xpath") : field.getAttribute("value"); + } + + if ("weight".equals(attribute)) { + if (!(field.hasAttribute(attribute))) return; + + value = field.hasAttribute(attribute) ? field.getAttribute(attribute) : ""; + } + + if (value == null) { + value = field.getAttribute(attribute); + } + + t.put(field.getAttribute("name").toLowerCase(), attribute, value); + } + } + }); + return t; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/MetadataReference.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/MetadataReference.java new file mode 100644 index 0000000..b65e668 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/MetadataReference.java @@ -0,0 +1,82 @@ +package eu.dnetlib.functionality.index.utils; + + +import com.google.common.collect.ComparisonChain; + +/** + * Container class for metadata format, layout and interpretation. + * + * @author claudio + * + */ +public class MetadataReference { + + /** + * Metadata format. + */ + private String format; + + /** + * Metadata layout. + */ + private String layout; + + /** + * Metadata interpretation. + */ + private String interpretation; + + /** + * Constructor for MetadataReference. + * + * @param format + * Metadata format + * @param layout + * Metadata layout + * @param interpretation + * Metadata interpretation + */ + public MetadataReference(final String format, final String layout, final String interpretation) { + this.format = format; + this.layout = layout; + this.interpretation = interpretation; + } + + public String getFormat() { + return format; + } + + public String getLayout() { + return layout; + } + + public String getInterpretation() { + return interpretation; + } + + @Override + public boolean equals(final Object that) { + + if (!(that instanceof MetadataReference)) + return false; + + final MetadataReference mdRef = (MetadataReference) that; + + return ComparisonChain.start() + .compare(this.format, mdRef.getFormat()) + .compare(this.layout, mdRef.getLayout()) + .compare(this.interpretation, mdRef.getInterpretation()) + .result() == 0; + } + + @Override + public int hashCode() { + return getFormat().hashCode() + getLayout().hashCode() + getInterpretation().hashCode(); + } + + @Override + public String toString() { + return getFormat() + "-" + getLayout() + "-" + getInterpretation(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/MetadataReferenceFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/MetadataReferenceFactory.java new file mode 100644 index 0000000..ed2955c --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/MetadataReferenceFactory.java @@ -0,0 +1,17 @@ +package eu.dnetlib.functionality.index.utils; + + +public class MetadataReferenceFactory { + + public static MetadataReference getMetadata(final String format, final String layout, final String interpretation) { + return new MetadataReference(format, layout, interpretation); + } + + public static MetadataReference decodeMetadata(final String encoded) { + String[] split = encoded.split("-"); + if (split.length == 3) return getMetadata(split[0], split[1], split[2]); + + throw new IllegalStateException("malformed metadata reference: " + encoded); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/ServiceTools.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/ServiceTools.java new file mode 100644 index 0000000..aa86612 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/ServiceTools.java @@ -0,0 +1,175 @@ +package eu.dnetlib.functionality.index.utils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Resource; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import eu.dnetlib.data.provision.index.rmi.IndexServiceException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpDocumentNotFoundException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.is.registry.ISRegistryDocumentNotFoundException; +import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException; +import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService; +import eu.dnetlib.enabling.locators.UniqueServiceLocator; +import eu.dnetlib.functionality.index.client.IndexClientException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class ServiceTools { + + private static final Log log = LogFactory.getLog(ServiceTools.class); + + @Resource + private UniqueServiceLocator serviceLocator; + + @Resource + private MetadataReferenceFactory mdFactory; + + public List listMDRefs() throws IndexClientException { + return Lists.newArrayList(Iterables.transform(listMDRefsAsString(), s -> mdFactory.decodeMetadata(s))); + } + + private List quickSearchProfile(final String xquery) throws IndexClientException { + try { + return serviceLocator.getService(ISLookUpService.class).quickSearchProfile(xquery); + } catch (ISLookUpException e) { + throw new IndexClientException(e); + } + } + + public MetadataReference getMetadataRef(final String dsId) throws IndexServiceException { + + final String xquery = "for $x in //RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value='" + dsId + "']//CONFIGURATION " + "return concat(" + + "$x/METADATA_FORMAT/text(),'-'," + "$x/METADATA_FORMAT_LAYOUT/text(),'-'," + "$x/METADATA_FORMAT_INTERPRETATION/text())"; + return mdFactory.decodeMetadata(getResourceProfileByQuery(xquery)); + } + + private String getResourceProfileByQuery(final String xquery) throws IndexServiceException { + try { + return serviceLocator.getService(ISLookUpService.class).getResourceProfileByQuery(xquery); + } catch (ISLookUpDocumentNotFoundException e) { + throw new IndexServiceException(e); + } catch (ISLookUpException e) { + throw new IndexServiceException(e); + } + } + + public String getIndexFields(final String dsId) throws IndexServiceException { + + return getIndexFields(getMetadataRef(dsId)); + } + + public String getIndexFields(final MetadataReference mdRef) { + + final String xquery = "for $x in collection('')/RESOURCE_PROFILE/BODY[CONFIGURATION/NAME='" + mdRef.getFormat() + + "'] return $x/STATUS/LAYOUTS/LAYOUT[@name='" + mdRef.getLayout() + "']/FIELDS"; + try { + return getResourceProfileByQuery(xquery); + } catch (IndexServiceException e) { + log.warn("couldn't find Metadata format profile matching specs: " + mdRef.toString()); + throw new RuntimeException(e); + } + } + + public List listDsIds() throws IndexClientException { + final String xquery = "//RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='IndexDSResourceType']//RESOURCE_IDENTIFIER/@value/string()"; + return quickSearchProfile(xquery); + } + + private List listMDRefsAsString() throws IndexClientException { + final String xquery = "for $x in //RESOURCE_PROFILE[.//RESOURCE_TYPE/@value='MDFormatDSResourceType'] \n" + + "let $format:= $x//CONFIGURATION/NAME/string() \n" + + "let $interpretation:= $x//CONFIGURATION/INTERPRETATION/text() \n" + + "for $y in $x//LAYOUTS/LAYOUT \n" + + " let $layout:= $y/@name/string() \n" + + " return concat($format,'-',$layout,'-',$interpretation) "; + return quickSearchProfile(xquery); + } + + public Map getIndexProperties(final String backendId) throws IndexClientException { + + String query = "for $x in /RESOURCE_PROFILE[.//RESOURCE_TYPE/@value=\"IndexServiceResourceType\"]//SERVICE_PROPERTIES/PROPERTY" + + " return concat($x/@key/string(),\":::\", $x/@value/string())"; + Map indexProperties = new HashMap(); + try { + List results = serviceLocator.getService(ISLookUpService.class).quickSearchProfile(query); + if (results != null) { + for (String s : results) { + String[] values = s.split(":::"); + if (values != null && values.length == 2) { + String key = values[0]; + String value = values[1]; + if (StringUtils.startsWith(key, backendId)) { + indexProperties.put(StringUtils.substringAfter(key, backendId + ":"), value); + } + } + } + } + return indexProperties; + } catch (ISLookUpException e) { + throw new IndexClientException(); + } + } + + public String registerProfile(final String resourceProfile) throws IndexServiceException { + try { + return serviceLocator.getService(ISRegistryService.class).registerProfile(resourceProfile); + } catch (ISRegistryException e) { + throw new IndexServiceException(e); + } + } + + public boolean incrementHandledDataStructures(final String backendId) throws IndexServiceException { + final String xquery = "let $x := //RESOURCE_PROFILE[HEADER/PROTOCOLS/PROTOCOL/@name='" + backendId + "']," + + "$tot := $x//STATUS/HANDLED_DATASTRUCTURE/number() + 1 " + "return update replace $x//STATUS/HANDLED_DATASTRUCTURE with " + + "{$tot}"; + + log.info("performing increment of HANDLED_DATASTRUCTURE"); + return executeXUpdate(xquery); + } + + private boolean executeXUpdate(final String xquery) throws IndexServiceException { + try { + return serviceLocator.getService(ISRegistryService.class).executeXUpdate(xquery); + } catch (ISRegistryException e) { + throw new IndexServiceException(e); + } + } + + public String getServiceAddress(final String backendId) { + final String xquery = "let $x := //RESOURCE_PROFILE[HEADER/PROTOCOLS/PROTOCOL/@name='" + backendId + "']" + + "return $x//PROTOCOL[./@name='SOAP']/@address/string()"; + try { + return getResourceProfileByQuery(xquery); + } catch (IndexServiceException e) { + log.warn("couldn't find service Address for index Service with protocol: " + backendId); + return ""; + } + } + + public boolean deleteIndexDS(final String dsId) throws IndexServiceException { + try { + return serviceLocator.getService(ISRegistryService.class).deleteProfile(dsId); + } catch (ISRegistryDocumentNotFoundException e) { + throw new IndexServiceException(e); + } catch (ISRegistryException e) { + throw new IndexServiceException(e); + } + } + + public List getBackendIds(final MetadataReference mdRef) throws IndexServiceException { + String query = "distinct-values(//RESOURCE_PROFILE[.//METADATA_FORMAT='%s' and .//METADATA_FORMAT_LAYOUT='%s' and .//METADATA_FORMAT_INTERPRETATION='%s']//BACKEND/@ID/string())"; + try { + String instanceQuery = String.format(query, mdRef.getFormat(), mdRef.getLayout(), mdRef.getInterpretation()); + log.debug("Executing query to IS: " + instanceQuery); + return serviceLocator.getService(ISLookUpService.class).quickSearchProfile(instanceQuery); + } catch (ISLookUpException e) { + throw new IndexServiceException(e); + } + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/ZkServers.java b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/ZkServers.java new file mode 100644 index 0000000..bafaaf7 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/functionality/index/utils/ZkServers.java @@ -0,0 +1,58 @@ +package eu.dnetlib.functionality.index.utils; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.List; +import java.util.Optional; + +public class ZkServers { + + private static final Log log = LogFactory.getLog(ZkServers.class); + public static final String SEPARATOR = "/"; + + private List hosts; + + private Optional chroot; + + public static ZkServers newInstance(final String zkUrl) { + + //quorum0:2182,quorum1:2182,quorum2:2182,quorum3:2182,quorum4:2182/solr-dev-openaire + String urls = zkUrl; + final Optional chRoot = Optional.of(SEPARATOR + StringUtils.substringAfterLast(zkUrl, SEPARATOR)); + if (chRoot.isPresent() && StringUtils.isNotBlank(chRoot.get())) { + log.debug(String.format("found zk chroot %s", chRoot)); + urls = zkUrl.replace(chRoot.get(), ""); + } + + final List urlList = Lists.newArrayList(Splitter.on(",").omitEmptyStrings().split(urls)); + log.debug(String.format("zk urls %s", zkUrl)); + + return new ZkServers(urlList, chRoot); + } + + public ZkServers(List hosts, Optional chroot) { + this.hosts = hosts; + this.chroot = chroot; + } + + public List getHosts() { + return hosts; + } + + public void setHosts(List hosts) { + this.hosts = hosts; + } + + public Optional getChroot() { + return chroot; + } + + public void setChroot(Optional chroot) { + this.chroot = chroot; + } +} + diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/aop/MethodCacheInterceptor.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/aop/MethodCacheInterceptor.java new file mode 100644 index 0000000..07cdc96 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/aop/MethodCacheInterceptor.java @@ -0,0 +1,139 @@ +package eu.dnetlib.springutils.aop; + +/* + * Copyright 2002-2004 the original author or authors. + * + * Licensed 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. + */ + +import java.io.Serializable; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.Element; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; + +/** + * This AOP interceptor allows to implement method call memoization, e.g. cache each single invocation of a + * method or group of methods (possibly all of them) of a given object. It associates the method call result to a hash + * based on the parameter names, which should somehow represent their identity when converted toString(). + * + */ +public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean { + /** + * logger. + */ + private static final Log log = LogFactory.getLog(MethodCacheInterceptor.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * cache. + */ + private transient Cache cache; + /** + * if true, the cache is updated every time and the method is executed even if the result is already cached. + */ + private boolean writeOnly; + + /** + * construct a read-write cache interceptor. + */ + public MethodCacheInterceptor() { + this.writeOnly = false; + } + + /** + * sets cache name to be used. + * + * @param cache + * cache instance + */ + public void setCache(final Cache cache) { + this.cache = cache; + } + + /** + * Checks if required attributes are provided. + */ + @Override + public void afterPropertiesSet() { + Assert.notNull(cache, "A cache is required. Use setCache(Cache) to provide one."); + } + + /** + * main method caches method result if method is configured for caching. method results must be serializable + * + * @param invocation + * method invocation object + * @throws Throwable + * transparent + * @return transparent + */ + @Override + public Object invoke(final MethodInvocation invocation) throws Throwable { + final String targetName = invocation.getThis().getClass().getName(); + final String methodName = invocation.getMethod().getName(); + final Object[] arguments = invocation.getArguments(); + Object result; + + log.debug("looking for method result in cache: " + isWriteOnly()); + final String cacheKey = getCacheKey(targetName, methodName, arguments); + Element element = cache.get(cacheKey); + if (element == null || isWriteOnly()) { + // call target/sub-interceptor + log.debug("calling intercepted method"); + result = invocation.proceed(); + + // cache method result + log.debug("caching result"); + element = new Element(cacheKey, (Serializable) result); + cache.put(element); + } else { + log.debug("found in method cache"); + } + return element.getObjectValue(); + } + + /** + * creates cache key: targetName.methodName.argument0.argument1... + * + * @param targetName target name + * @param methodName method name + * @param arguments actual arguments + * @return cache key based on the parameters + */ + private String getCacheKey(final String targetName, final String methodName, final Object[] arguments) { + final StringBuffer sbuf = new StringBuffer(); + sbuf.append(targetName).append('.').append(methodName); + if ((arguments != null) && (arguments.length != 0)) { + for (int i = 0; i < arguments.length; i++) { + sbuf.append('.').append(arguments[i]); + } + } + + return sbuf.toString(); + } + + public boolean isWriteOnly() { + return writeOnly; + } + + public void setWriteOnly(final boolean writeOnly) { + this.writeOnly = writeOnly; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/aop/MethodInvokingInitializer.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/aop/MethodInvokingInitializer.java new file mode 100644 index 0000000..071dd0a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/aop/MethodInvokingInitializer.java @@ -0,0 +1,52 @@ +package eu.dnetlib.springutils.aop; + +import java.lang.reflect.Method; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Required; + +/** + * Ever tried to use an init-method on a bean which should be wrapped by a aop transaction? This helper class allow you + * to invoke an init method through the aop proxy, thus starting the transactions or other stuff, something the + * plain spring init method doesn't. + * + * @author marko + * + */ +public class MethodInvokingInitializer implements InitializingBean { + + private static final Log log = LogFactory.getLog(MethodInvokingInitializer.class); // NOPMD by marko on 11/24/08 5:02 PM + + private Object target; + private String methodName; + + public Object getTarget() { + return target; + } + + @Required + public void setTarget(Object target) { + this.target = target; + } + + public String getMethodName() { + return methodName; + } + + @Required + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + @Override + public void afterPropertiesSet() throws Exception { + log.info("starting"); + System.out.println("starting"); + + Method method = target.getClass().getMethod(methodName, new Class[] {}); + method.invoke(target, new Object[] {}); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/beans/factory/NullBeanFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/beans/factory/NullBeanFactory.java new file mode 100644 index 0000000..70de4f9 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/beans/factory/NullBeanFactory.java @@ -0,0 +1,44 @@ +package eu.dnetlib.springutils.beans.factory; + +import org.springframework.beans.factory.FactoryBean; + +/** + * Use this class if you want to have a named bean defined as a null value. (Especially useful in some unit tests, when the bean to be + * tested has \@Required properties) + * + * @author marko + * + */ +public class NullBeanFactory implements FactoryBean { + + /** + * {@inheritDoc} + * + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + @Override + public Void getObject() throws Exception { + return null; + } + + /** + * {@inheritDoc} + * + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + @Override + public Class getObjectType() { + return null; + } + + /** + * {@inheritDoc} + * + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + @Override + public boolean isSingleton() { + return true; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/beans/factory/PrototypeFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/beans/factory/PrototypeFactory.java new file mode 100644 index 0000000..8a8afea --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/beans/factory/PrototypeFactory.java @@ -0,0 +1,39 @@ +package eu.dnetlib.springutils.beans.factory; + +import eu.dnetlib.miscutils.factory.Factory; + +/** + * Helper class to support simple Factory definitions with prototype scoped instances: + * + *

    Example:

    + * + *
    + * 	<bean id="resultSetDescriptorPrototype" class="eu.dnetlib.enabling.resultset.push.ResultSetDescriptor"
    + *		p:rangeLength="200" scope="prototype" />
    + *
    + *	<bean id="resultSetDescriptorFactory" class="eu.dnetlib.springutils.beans.factory.PrototypeFactory">
    + *		<lookup-method name="newInstance" bean="resultSetDescriptorPrototype" />
    + *	</bean>
    + * 
    + * + * @author marko + * + * @param + */ +public class PrototypeFactory implements Factory { + + /** + * {@inheritDoc} + * + *

    + * meant to be replaced by spring method injection. + *

    + * . + * + * @see eu.dnetlib.miscutils.factory.Factory#newInstance() + */ + @Override + public T newInstance() { + return null; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/beans/factory/SingleListFactoryBean.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/beans/factory/SingleListFactoryBean.java new file mode 100644 index 0000000..f6d1ba0 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/beans/factory/SingleListFactoryBean.java @@ -0,0 +1,32 @@ +package eu.dnetlib.springutils.beans.factory; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.config.ListFactoryBean; + +/** + * this bean factory allows to quickly declare a list object containing only one element. This is useful + * when we need to avoid a nested <property><list>...</list></property>. + * + *

    + * For example using the template custom beans defined in this library we currently cannot expand values deeply nested + * inside &list> elements, so we need to resort to this trick in order to populate list properties with a single list element. + *

    + * + * @author marko + * + */ +public class SingleListFactoryBean extends ListFactoryBean { + + /** + * create a list with one value. + * + * @param value value of the singleton list + */ + public void setValue(Object value) { + List list = new ArrayList(); + list.add(value); + setSourceList(list); + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/collections/ResourceReaderCollection.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/collections/ResourceReaderCollection.java new file mode 100644 index 0000000..2c19711 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/collections/ResourceReaderCollection.java @@ -0,0 +1,42 @@ +package eu.dnetlib.springutils.collections; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Collection; + +import org.apache.commons.io.IOUtils; +import org.springframework.core.io.Resource; + +import eu.dnetlib.miscutils.collections.MappedCollection; +import eu.dnetlib.miscutils.functional.UnaryFunction; + +/** + * very useful in unit tests when you want to read many resources obtained from the spring resource loader into a + * collection of strings. + * + * @author marko + * + */ +public class ResourceReaderCollection extends MappedCollection { + + /** + * construct a ResourceReaderCollection from a collection of resources. + * @param coll + */ + public ResourceReaderCollection(Collection coll) { + super(coll, new UnaryFunction() { + + @Override + public String evaluate(Resource resource) { + try { + final StringWriter buffer = new StringWriter(); + IOUtils.copy(resource.getInputStream(), buffer); + return buffer.toString(); + } catch (IOException e) { + throw new IllegalArgumentException("cannot read resource", e); + } + } + }); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ChainPropertyFinder.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ChainPropertyFinder.java new file mode 100644 index 0000000..41b0b2b --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ChainPropertyFinder.java @@ -0,0 +1,36 @@ +package eu.dnetlib.springutils.condbean; + +import java.util.Collection; + +/** + * Chain property finder. + * + * @author marko + * + */ +public class ChainPropertyFinder implements PropertyFinder { + + /** + * finders. + */ + Collection finders; + + @Override + public String getProperty(final String name) { + for (final PropertyFinder finder : finders) { + final String res = finder.getProperty(name); + if (res != null) + return res; + } + return null; + } + + public Collection getFinders() { + return finders; + } + + public void setFinders(Collection finders) { + this.finders = finders; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionExpressionParser.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionExpressionParser.java new file mode 100644 index 0000000..f7a0f0d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionExpressionParser.java @@ -0,0 +1,22 @@ +package eu.dnetlib.springutils.condbean; + +/** + * A ConditionExpressionParser parses simple expressions like + * + * ${some.property} > 10 and !${some.other.property}. + * + * @author marko + * + */ +public interface ConditionExpressionParser { + + /** + * return the boolean value of a given expression. + * + * @param expression + * expression source string + * @return expression truth value + */ + boolean expressionValue(String expression); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionalBeanAttributeDecorator.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionalBeanAttributeDecorator.java new file mode 100644 index 0000000..130d450 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionalBeanAttributeDecorator.java @@ -0,0 +1,30 @@ +package eu.dnetlib.springutils.condbean; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.xml.BeanDefinitionDecorator; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Node; + +/** + * + * TODO: unfinished. + * + * @author marko + * + */ +public class ConditionalBeanAttributeDecorator implements BeanDefinitionDecorator { + + private static final Log log = LogFactory.getLog(ConditionalBeanAttributeDecorator.class); // NOPMD by marko on 11/24/08 5:02 PM + + @Override + public BeanDefinitionHolder decorate(final Node source, final BeanDefinitionHolder holder, final ParserContext ctx) { + + final String name = holder.getBeanName(); + log.fatal("NAME: " + name); + + return holder; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionalBeanDefinitionParser.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionalBeanDefinitionParser.java new file mode 100644 index 0000000..e8023c4 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionalBeanDefinitionParser.java @@ -0,0 +1,117 @@ +package eu.dnetlib.springutils.condbean; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +import com.google.common.collect.Lists; + +import eu.dnetlib.springutils.condbean.parser.CondBeanParser; +import eu.dnetlib.springutils.condbean.parser.RunccExpressionParser; + +/** + * Taken from http://robertmaldon.blogspot.com/2007/04/conditionally-defining-spring-beans.html . + * + * modified to fix a problem caused by http://jira.springframework.org/browse/SPR-2955 improved expression parsing + * + * @author marko + * + */ +public class ConditionalBeanDefinitionParser implements BeanDefinitionParser { + + /** + * logger. + */ + @SuppressWarnings("unused") + // NOPMD by marko on 11/24/08 5:02 PM + private static final Log log = LogFactory.getLog(ConditionalBeanDefinitionParser.class); + + /** + * expression parser. + */ + private transient RunccExpressionParser expressionParser = null; + + public void init(ResourceLoader resourceLoader) { + if(expressionParser != null) + return; + + final ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); + + ChainPropertyFinder chain = new ChainPropertyFinder(); + + ResourcePropertyFinder res1 = new ResourcePropertyFinder(); + res1.setResource(resolver.getResource("classpath:/eu/dnetlib/cnr-default.properties")); + + ResourcePropertyFinder res2 = new ResourcePropertyFinder(); + res2.setResource(resolver.getResource("classpath:/eu/dnetlib/cnr-site.properties")); + + ResourcePropertyFinder res3 = new ResourcePropertyFinder(); + res3.setResource(resolver.getResource("classpath:/cnr-override.properties")); + + chain.setFinders(Lists.newArrayList(new SystemPropertiesFinder(), res3, res2, res1)); + + CondBeanParser condBeanParser = new CondBeanParser(); + condBeanParser.setPropertyFinder(chain); + + expressionParser = new RunccExpressionParser(); + expressionParser.setCondBeanParser(condBeanParser); + } + + /** + * Parse the "cond" element and check the mandatory "test" attribute. If the system property named by test is null + * or empty (i.e. not defined) then return null, which is the same as not defining the bean. + * + * @param element + * element to parse + * @param parserContext + * spring parser context + * @return registered bean or null. + */ + @Override + public BeanDefinition parse(final Element element, final ParserContext parserContext) { + init(parserContext.getReaderContext().getResourceLoader()); + + if (DomUtils.nodeNameEquals(element, "cond")) { + final String test = element.getAttribute("test"); + if (expressionParser.expressionValue(test)) { + final Element beanElement = DomUtils.getChildElementByTagName(element, "bean"); + return parseAndRegisterBean(beanElement, parserContext); + } + } + + return null; + } + + /** + * the real job of registering the child element of this conditional bean is performed here. + * + * @param element + * element + * @param parserContext + * spring parser context + * @return registered bean. + */ + private BeanDefinition parseAndRegisterBean(final Element element, final ParserContext parserContext) { + if (element == null) + throw new IllegalStateException("trying to register a null bean"); + + final BeanDefinitionParserDelegate delegate = parserContext.getDelegate(); + BeanDefinitionHolder holder = delegate.parseBeanDefinitionElement(element); + BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry()); + + // necessary because of http://jira.springframework.org/browse/SPR-2955 + holder = delegate.decorateBeanDefinitionIfRequired(element, holder); + return holder.getBeanDefinition(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionalBeanNamespaceHandler.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionalBeanNamespaceHandler.java new file mode 100644 index 0000000..c1be005 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ConditionalBeanNamespaceHandler.java @@ -0,0 +1,22 @@ +package eu.dnetlib.springutils.condbean; + +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + +/** + * register conditional bean parsers for the condbean namespace and schema. + * + * @author marko + * + */ +public class ConditionalBeanNamespaceHandler extends NamespaceHandlerSupport { + /** + * {@inheritDoc} + * @see org.springframework.beans.factory.xml.NamespaceHandler#init() + */ + @Override + public void init() { + super.registerBeanDefinitionDecoratorForAttribute("ifdefined", new ConditionalBeanAttributeDecorator()); + + super.registerBeanDefinitionParser("cond", new ConditionalBeanDefinitionParser()); + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/JParsecConditionExpressionParser.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/JParsecConditionExpressionParser.java new file mode 100644 index 0000000..affe94b --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/JParsecConditionExpressionParser.java @@ -0,0 +1,25 @@ +package eu.dnetlib.springutils.condbean; + +import org.codehaus.jparsec.Terminals; + +/** + * This class implements a simple ConditionExpressionParser using jparsec. + * + * @author marko + * + */ + +@SuppressWarnings("unused") +public class JParsecConditionExpressionParser implements ConditionExpressionParser { + + private static final String[] OPERATORS = { "&&", "||", "+", "-", "*", "/", ">", "<", ">=", "<=", // and all other operators. + }; + private static final String[] KEYWORDS = { "true", "false" }; + private static final Terminals TERMINALS = Terminals.caseSensitive(OPERATORS, KEYWORDS); + + @Override + public boolean expressionValue(final String expression) { + return false; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/PropertyFinder.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/PropertyFinder.java new file mode 100644 index 0000000..8cea92d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/PropertyFinder.java @@ -0,0 +1,5 @@ +package eu.dnetlib.springutils.condbean; + +public interface PropertyFinder { + String getProperty(String name); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ResourcePropertyFinder.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ResourcePropertyFinder.java new file mode 100644 index 0000000..7f99afb --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/ResourcePropertyFinder.java @@ -0,0 +1,60 @@ +package eu.dnetlib.springutils.condbean; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.core.io.Resource; + +/** + * Load properties from a spring resource (classpath, filesystem, etc). + * + * @author marko + * + */ +public class ResourcePropertyFinder implements PropertyFinder { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(ResourcePropertyFinder.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * properties are loaded from this resource. + */ + private Resource resource; + + /** + * {@inheritDoc} + * @see eu.dnetlib.springutils.condbean.PropertyFinder#getProperty(java.lang.String) + */ + @Override + public String getProperty(final String name) { + if(resource == null) + return null; + try { + final Properties props = new Properties(); + props.load(resource.getInputStream()); + + final String res = props.getProperty(name); + if (res != null && !res.isEmpty()) + return res; + } catch(final FileNotFoundException e) { + return null; + } catch (final IOException e) { + log.warn("cannot open properties", e); + } + return null; + } + + public Resource getResource() { + return resource; + } + + public void setResource(final Resource resource) { + this.resource = resource; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/SystemPropertiesFinder.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/SystemPropertiesFinder.java new file mode 100644 index 0000000..c756e16 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/SystemPropertiesFinder.java @@ -0,0 +1,10 @@ +package eu.dnetlib.springutils.condbean; + +public class SystemPropertiesFinder implements PropertyFinder { + + @Override + public String getProperty(String name) { + return System.getProperty(name); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/TrivialConditionExpressionParser.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/TrivialConditionExpressionParser.java new file mode 100644 index 0000000..60b9902 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/TrivialConditionExpressionParser.java @@ -0,0 +1,80 @@ +package eu.dnetlib.springutils.condbean; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This is the original implementation of Robert Maldon's property expression parser, factored out in a + * ConditionExpressionParser so that we can easly replace it with a more powerful parser. + * + * @see JParsecConditionExpression + * + * @author marko + * + */ +public class TrivialConditionExpressionParser implements ConditionExpressionParser { + private static final Log log = LogFactory.getLog(TrivialConditionExpressionParser.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** Default placeholder prefix: "${". */ + public static final String PH_PREFIX = "${"; + + /** Default placeholder suffix: "}". */ + public static final String PH_SUFFIX = "}"; + + private PropertyFinder finder; + + @Override + public boolean expressionValue(final String expression) { + log.debug("EXPRESSION: " + expression); + log.debug("EXPRESSION VALUE: " + getProperty(expression)); + return !StringUtils.isEmpty(getProperty(expression)); + } + + protected String getProperty(final String strVal) { + if (StringUtils.isEmpty(strVal)) + return null; + + if (strVal.startsWith(PH_PREFIX) && strVal.endsWith(PH_SUFFIX)) + return basicGetProperty(strVal.substring(PH_PREFIX.length(), strVal.length() - PH_SUFFIX.length())); + + return basicGetProperty(strVal); + } + + protected String basicGetProperty(final String name) { + return finder.getProperty(name); + } + + protected String xxbasicGetProperty(final String name) { + // TODO: merge properties from many files (wildcards) and provide a configuration + final Properties props = new Properties(); + try { + final InputStream input = TrivialConditionExpressionParser.class.getResourceAsStream("/eu/dnetlib/cnr-default.properties"); + if (input == null) + log.warn("cannot open properties (null)"); + else + props.load(input); + + } catch (IOException e) { + log.warn("cannot open properties", e); + } + final String res = props.getProperty(name); + if (res != null && !res.isEmpty()) + return res; + + return System.getProperty(name); + } + + public PropertyFinder getFinder() { + return finder; + } + + public void setFinder(PropertyFinder finder) { + this.finder = finder; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/CondBeanParser.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/CondBeanParser.java new file mode 100644 index 0000000..1775d6e --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/CondBeanParser.java @@ -0,0 +1,86 @@ +package eu.dnetlib.springutils.condbean.parser; + +import eu.dnetlib.springutils.condbean.PropertyFinder; +import eu.dnetlib.springutils.condbean.parser.ast.AbstractExpression; +import eu.dnetlib.springutils.condbean.parser.ast.AndExpression; +import eu.dnetlib.springutils.condbean.parser.ast.BooleanNode; +import eu.dnetlib.springutils.condbean.parser.ast.EqualityExpression; +import eu.dnetlib.springutils.condbean.parser.ast.IdentifierNode; +import eu.dnetlib.springutils.condbean.parser.ast.IsdefExpression; +import eu.dnetlib.springutils.condbean.parser.ast.NotExpression; +import eu.dnetlib.springutils.condbean.parser.ast.NumberNode; +import eu.dnetlib.springutils.condbean.parser.ast.OrExpression; +import eu.dnetlib.springutils.condbean.parser.ast.QualNameNode; +import eu.dnetlib.springutils.condbean.parser.ast.RelationalExpression; +import eu.dnetlib.springutils.condbean.parser.ast.StringTypeNode; +import eu.dnetlib.springutils.condbean.parser.ast.SysProperty; +import fri.patterns.interpreter.parsergenerator.semantics.ReflectSemantic; + +public class CondBeanParser extends ReflectSemantic { // NOPMD + + private PropertyFinder propertyFinder; + + public CondBeanParser() { + } + + public Object logicalOrExpression(final Object orExp, final Object logicalor, final Object andExp) { + return new OrExpression((AbstractExpression) orExp, (AbstractExpression) andExp); + } + + public Object logicalAndExpression(final Object andExp, final Object logicaland, final Object equalExpr) { + return new AndExpression((AbstractExpression) andExp, (AbstractExpression) equalExpr); + } + + public Object equalityExpression(final Object equalExpr, final Object operator, final Object relExpr) { + return new EqualityExpression((AbstractExpression) equalExpr, (AbstractExpression) relExpr, (String) operator); + } + + public Object relationalExpression(final Object relExpr, final Object operator, final Object unaryExpression) { + return new RelationalExpression((AbstractExpression) relExpr, (AbstractExpression) unaryExpression, (String) operator); + } + + public Object unaryExpression(final Object lcb, final Object literal, final Object rcb) { + return new SysProperty(propertyFinder, (QualNameNode) literal); + } + + public Object unaryExpression(final Object exmark, final Object unaryExpression) { + return new NotExpression((AbstractExpression) unaryExpression); + } + + public Object simpleType(final Object literals) { + return new IdentifierNode((String) literals); + } + + public Object qualifiedType(final Object literals) { + return new QualNameNode((String) literals); + } + + public Object stringType(final Object astring) { + return new StringTypeNode((String) astring); + } + + public Object booleanType(final Object literals) { + return new BooleanNode((String) literals); + } + + public Object unaryExpression(final Object isdef, final Object leftpar, final Object identifier, final Object rightpar) { + return new IsdefExpression((AbstractExpression) identifier); + } + + public Object primaryExpression(final Object leftpar, final Object expression, final Object rightpar) { + return expression; + } + + public Object numberType(final Object number) { + return new NumberNode((String) number); + } + + public PropertyFinder getPropertyFinder() { + return propertyFinder; + } + + public void setPropertyFinder(PropertyFinder propertyFinder) { + this.propertyFinder = propertyFinder; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/RunccExpressionParser.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/RunccExpressionParser.java new file mode 100644 index 0000000..3831b15 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/RunccExpressionParser.java @@ -0,0 +1,124 @@ +package eu.dnetlib.springutils.condbean.parser; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import eu.dnetlib.springutils.condbean.ConditionExpressionParser; +import eu.dnetlib.springutils.condbean.parser.ast.AbstractExpression; +import eu.dnetlib.springutils.condbean.parser.ast.Converter; +import fri.patterns.interpreter.parsergenerator.Lexer; +import fri.patterns.interpreter.parsergenerator.Parser; +import fri.patterns.interpreter.parsergenerator.ParserTables; +import fri.patterns.interpreter.parsergenerator.lexer.LexerException; +import fri.patterns.interpreter.parsergenerator.parsertables.LALRParserTables; +import fri.patterns.interpreter.parsergenerator.parsertables.ParserBuildException; +import fri.patterns.interpreter.parsergenerator.syntax.SyntaxException; +import fri.patterns.interpreter.parsergenerator.syntax.builder.SyntaxBuilder; + +public class RunccExpressionParser implements ConditionExpressionParser { + + private static final Log log = LogFactory //NOPMD + .getLog(RunccExpressionParser.class); + + private Parser parser; + private InputStream syntaxInput; + private SyntaxBuilder builder; + private Lexer lexer; + private ParserTables tables; + + private CondBeanParser condBeanParser; + + @Override + public boolean expressionValue(final String expression) { + try { + parser.setInput(expression); + if (!parser.parse(condBeanParser)) + log.fatal("Syntax errors in the expression"); + } catch (IOException ioExc) { + log.fatal("Error in evaluating the expression", ioExc); + } + return Converter.toBoolean(((AbstractExpression) parser.getResult()).evaluate()); + } + + public AbstractExpression getTopRule(final String expression) { + try { + parser.setInput(expression); + if (!parser.parse(new CondBeanParser())) + log.fatal("Syntax errors in the expression"); + } catch (IOException ioExc) { + log.fatal("Error in evaluating the expression", ioExc); + } + return (AbstractExpression) parser.getResult(); + } + + public RunccExpressionParser() { + try { + syntaxInput = CondBeanParser.class.getResourceAsStream("CondBeanParser.syntax"); + builder = new SyntaxBuilder(syntaxInput); + lexer = builder.getLexer(); + tables = new LALRParserTables(builder.getParserSyntax()); + parser = new Parser(tables); + parser.setLexer(lexer); + } catch (SyntaxException syntEx) { + log.fatal("Errors in the syntax specification!", syntEx); + } catch (LexerException lexEx) { + log.fatal("Lexer error!", lexEx); + } catch (ParserBuildException parsBuilEx) { + log.fatal("Cannot create the Parser, errors in the grammar specification!", parsBuilEx); + } catch (IOException ioEx) { + throw new IllegalStateException("Cannot read the grammar file!", ioEx); + } + } + + public Parser getParser() { + return parser; + } + + public void setParser(final Parser parser) { + this.parser = parser; + } + + public InputStream getSyntaxInput() { + return syntaxInput; + } + + public void setSyntaxInput(final InputStream syntaxInput) { + this.syntaxInput = syntaxInput; + } + + public SyntaxBuilder getBuilder() { + return builder; + } + + public void setBuilder(final SyntaxBuilder builder) { + this.builder = builder; + } + + public Lexer getLexer() { + return lexer; + } + + public void setLexer(final Lexer lexer) { + this.lexer = lexer; + } + + public ParserTables getTables() { + return tables; + } + + public void setTables(final ParserTables tables) { + this.tables = tables; + } + + public CondBeanParser getCondBeanParser() { + return condBeanParser; + } + + public void setCondBeanParser(CondBeanParser condBeanParser) { + this.condBeanParser = condBeanParser; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractBinaryExpression.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractBinaryExpression.java new file mode 100644 index 0000000..e702b7d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractBinaryExpression.java @@ -0,0 +1,29 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public abstract class AbstractBinaryExpression extends AbstractExpression { + private AbstractExpression left; + private AbstractExpression right; + + public AbstractBinaryExpression(final AbstractExpression left, final AbstractExpression right) { + super(); + this.left = left; + this.right = right; + } + + public AbstractExpression getLeft() { + return left; + } + + public void setLeft(final AbstractExpression left) { + this.left = left; + } + + public AbstractExpression getRight() { + return right; + } + + public void setRight(final AbstractExpression right) { + this.right = right; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractExpression.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractExpression.java new file mode 100644 index 0000000..546efab --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractExpression.java @@ -0,0 +1,7 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public abstract class AbstractExpression { + + public abstract Object evaluate(); + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractTerminal.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractTerminal.java new file mode 100644 index 0000000..c7a14a6 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractTerminal.java @@ -0,0 +1,24 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public abstract class AbstractTerminal extends AbstractExpression { + + private String data; + + public AbstractTerminal(final String input) { + super(); + this.data = input; + } + + @Override + public Object evaluate() { + return data; + } + + public String getData() { + return data; + } + + public void setData(final String data) { + this.data = data; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractUnaryExpression.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractUnaryExpression.java new file mode 100644 index 0000000..43fe411 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AbstractUnaryExpression.java @@ -0,0 +1,20 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public abstract class AbstractUnaryExpression extends AbstractExpression { + + private AbstractExpression expression; + + public AbstractUnaryExpression(final AbstractExpression expression) { + super(); + this.expression = expression; + } + + public AbstractExpression getExpression() { + return expression; + } + + public void setExpression(final AbstractExpression expression) { + this.expression = expression; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AndExpression.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AndExpression.java new file mode 100644 index 0000000..0af1fc8 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/AndExpression.java @@ -0,0 +1,18 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class AndExpression extends AbstractBinaryExpression { + + private boolean eval() { + return Converter.toBoolean(getLeft().evaluate()) && Converter.toBoolean(getRight().evaluate()); + } + + public AndExpression(final AbstractExpression left, final AbstractExpression right) { + super(left, right); + } + + @Override + public Object evaluate() { + return eval(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/BooleanNode.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/BooleanNode.java new file mode 100644 index 0000000..51f245d --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/BooleanNode.java @@ -0,0 +1,17 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class BooleanNode extends AbstractTerminal { + + private final transient boolean bool; + + public BooleanNode(final String input) { + super(input); + bool = Boolean.parseBoolean(input); + } + + @Override + public Object evaluate() { + return bool; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/Converter.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/Converter.java new file mode 100644 index 0000000..c9dfda0 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/Converter.java @@ -0,0 +1,44 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public final class Converter { //NOPMD + + private Converter() { + + } + + public static boolean toBoolean(final Object obj) { + if (obj instanceof String) + return !((String) obj).isEmpty(); + else if (obj instanceof Boolean) + return ((Boolean) obj).booleanValue(); + else if (obj instanceof Integer || obj instanceof Double) { + return !((Double) (Double.parseDouble("0"))).equals(obj); + } else + return false; + } + + @SuppressWarnings("unchecked") + public static int compareTo(final Object obj1, final Object obj2) { //NOPMD + + /** + * This method has the following return values: + * + * - result of compareTo if the two objects are Comparable and of the same runtime class + * + */ + if (obj1 == null && obj2 == null) + return 0; + else if (obj1 == null) + return -1; + else if (obj2 == null) + return 1; + else if (obj1 == obj2)//NOPMD + return 0; + else if (obj1 instanceof Comparable && obj2 instanceof Comparable && obj1.getClass().equals(obj2.getClass())) + return ((Comparable) obj1).compareTo(obj2); + else + return (obj1.hashCode() > obj2.hashCode()) ? 1 : (obj1.hashCode() == obj2.hashCode() ? 0 : -1); + + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/EqualityExpression.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/EqualityExpression.java new file mode 100644 index 0000000..8f36928 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/EqualityExpression.java @@ -0,0 +1,35 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class EqualityExpression extends AbstractBinaryExpression { + + private String operator; + + private boolean eval() { + if (getLeft().evaluate() == null && getRight().evaluate() == null) + return true; + else if (getLeft().evaluate() == null || getRight().evaluate() == null) + return false; + return getLeft().evaluate().equals(getRight().evaluate()); + } + + public EqualityExpression(final AbstractExpression left, final AbstractExpression right, final String oper) { + super(left, right); + operator = oper; + // TODO Auto-generated constructor stub + } + + @Override + public Object evaluate() { + if ("==".equals(operator)) + return eval(); + return !eval(); + } + + public String getOperator() { + return operator; + } + + public void setOperator(final String operator) { + this.operator = operator; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/IdentifierNode.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/IdentifierNode.java new file mode 100644 index 0000000..9044b01 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/IdentifierNode.java @@ -0,0 +1,10 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class IdentifierNode extends AbstractTerminal { + + public IdentifierNode(final String data) { + super(data); + // TODO Auto-generated constructor stub + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/IsdefExpression.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/IsdefExpression.java new file mode 100644 index 0000000..55aa043 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/IsdefExpression.java @@ -0,0 +1,16 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class IsdefExpression extends AbstractUnaryExpression { + + public IsdefExpression(final AbstractExpression expression) { + super(expression); + // TODO Auto-generated constructor stub + } + + @Override + public Object evaluate() { + //TODO create and return Bean + return ""; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/LiteralNode.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/LiteralNode.java new file mode 100644 index 0000000..9afa35f --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/LiteralNode.java @@ -0,0 +1,10 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class LiteralNode extends AbstractTerminal { + + public LiteralNode(final String data) { + super(data); + // TODO Auto-generated constructor stub + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/NotExpression.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/NotExpression.java new file mode 100644 index 0000000..047d64a --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/NotExpression.java @@ -0,0 +1,15 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class NotExpression extends AbstractUnaryExpression { + + public NotExpression(final AbstractExpression expression) { + super(expression); + // TODO Auto-generated constructor stub + } + + @Override + public Object evaluate() { + return !Converter.toBoolean(getExpression().evaluate()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/NumberNode.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/NumberNode.java new file mode 100644 index 0000000..1d2734c --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/NumberNode.java @@ -0,0 +1,16 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class NumberNode extends AbstractTerminal { + + private final transient double num; + + public NumberNode(final String data) { + super(data); + num = Double.parseDouble(data); + } + + @Override + public Object evaluate() { + return num; + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/OrExpression.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/OrExpression.java new file mode 100644 index 0000000..23c0603 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/OrExpression.java @@ -0,0 +1,19 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class OrExpression extends AbstractBinaryExpression { + + private boolean eval() { + return Converter.toBoolean(getLeft().evaluate()) || Converter.toBoolean(getRight().evaluate()); + } + + public OrExpression(final AbstractExpression left, final AbstractExpression right) { + super(left, right); + // TODO Auto-generated constructor stub + } + + @Override + public Object evaluate() { + return eval(); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/QualNameNode.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/QualNameNode.java new file mode 100644 index 0000000..e737942 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/QualNameNode.java @@ -0,0 +1,9 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class QualNameNode extends LiteralNode { + + public QualNameNode(final String data) { + super(data); + // TODO Auto-generated constructor stub + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/RelationalExpression.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/RelationalExpression.java new file mode 100644 index 0000000..1800a29 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/RelationalExpression.java @@ -0,0 +1,40 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class RelationalExpression extends AbstractBinaryExpression { + + private String operator; + + private boolean eval() { + + int compResult; + + compResult = Converter.compareTo(getLeft().evaluate(), getRight().evaluate()); + + if (compResult > 0) + return (">".equals(operator) || ">=".equals(operator)); + else if (compResult < 0) + return ("<".equals(operator) || "<=".equals(operator)); + else + return ("<=".equals(operator) || ">=".equals(operator)); + } + + public RelationalExpression(final AbstractExpression left, final AbstractExpression right, final String oper) { + super(left, right); + operator = oper; + // TODO Auto-generated constructor stub + } + + @Override + public Object evaluate() { + return eval(); + } + + public String getOperator() { + return operator; + } + + public void setOperator(final String operator) { + this.operator = operator; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/StringTypeNode.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/StringTypeNode.java new file mode 100644 index 0000000..c827d74 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/StringTypeNode.java @@ -0,0 +1,8 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +public class StringTypeNode extends LiteralNode { + + public StringTypeNode(final String data) { + super(data.substring(1, data.length() - 1)); + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/SysProperty.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/SysProperty.java new file mode 100644 index 0000000..930c47f --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/condbean/parser/ast/SysProperty.java @@ -0,0 +1,22 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +import eu.dnetlib.springutils.condbean.PropertyFinder; + +public class SysProperty extends AbstractUnaryExpression { + + private PropertyFinder propertyFinder; + + public SysProperty(final PropertyFinder propertyFinder, final LiteralNode expression) { + super(expression); + this.propertyFinder = propertyFinder; + } + + @Override + public Object evaluate() { + if (propertyFinder == null) + return System.getProperty(((LiteralNode) getExpression()).getData()); + + return propertyFinder.getProperty(((LiteralNode) getExpression()).getData()); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/mvc/ServletBeanWrappingController.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/mvc/ServletBeanWrappingController.java new file mode 100644 index 0000000..fe7b591 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/mvc/ServletBeanWrappingController.java @@ -0,0 +1,196 @@ +package eu.dnetlib.springutils.mvc; + +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed 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. + */ + +import java.util.Enumeration; +import java.util.Properties; + +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.AbstractController; + + + +/** + * This class extends the ServletBeanWrapping controller with a direct access to the servletInstance property, so that you can configure + * the servlet like an ordinary spring bean. This is useful if you are porting legacy code to spring. + * + * Spring Controller implementation that wraps a servlet instance which it manages + * internally. Such a wrapped servlet is not known outside of this controller; + * its entire lifecycle is covered here (in contrast to {@link ServletForwardingController}). + * + *

    Useful to invoke an existing servlet via Spring's dispatching infrastructure, + * for example to apply Spring HandlerInterceptors to its requests. + * + *

    Note that Struts has a special requirement in that it parses web.xml + * to find its servlet mapping. Therefore, you need to specify the DispatcherServlet's + * servlet name as "servletName" on this controller, so that Struts finds the + * DispatcherServlet's mapping (thinking that it refers to the ActionServlet). + * + *

    Example: a DispatcherServlet XML context, forwarding "*.do" to the Struts + * ActionServlet wrapped by a ServletWrappingController. All such requests will go + * through the configured HandlerInterceptor chain (e.g. an OpenSessionInViewInterceptor). + * From the Struts point of view, everything will work as usual. + * + *

    + * <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    + *   <property name="interceptors">
    + *     <list>
    + *       <ref bean="openSessionInViewInterceptor"/>
    + *     </list>
    + *   </property>
    + *   <property name="mappings">
    + *     <props>
    + *       <prop key="*.do">strutsWrappingController</prop>
    + *     </props>
    + *   </property>
    + * </bean>
    + *
    + * <bean id="strutsWrappingController" class="org.springframework.web.servlet.mvc.ServletWrappingController">
    + *   <property name="servletClass">
    + *     <value>org.apache.struts.action.ActionServlet</value>
    + *   </property>
    + *   <property name="servletName">
    + *     <value>action</value>
    + *   </property>
    + *   <property name="initParameters">
    + *     <props>
    + *       <prop key="config">/WEB-INF/struts-config.xml</prop>
    + *     </props>
    + *   </property>
    + * </bean>
    + * + * @author Juergen Hoeller + * @since 1.1.1 + * @see ServletForwardingController + * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor + * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter + * @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewInterceptor + * @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter + */ +public class ServletBeanWrappingController extends AbstractController + implements InitializingBean, DisposableBean { + + private String servletName; + + private Properties initParameters = new Properties(); + + private Servlet servletInstance; + + + /** + * Set the name of the servlet to wrap. + * Default is the bean name of this controller. + */ + public void setServletName(String servletName) { + this.servletName = servletName; + } + + /** + * Specify init parameters for the servlet to wrap, + * as name-value pairs. + */ + public void setInitParameters(Properties initParameters) { + this.initParameters = initParameters; + } + + + /** + * Initialize the wrapped Servlet instance. + * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) + */ + @Override + public void afterPropertiesSet() throws Exception { + this.servletInstance.init(new DelegatingServletConfig()); + } + + + /** + * Invoke the the wrapped Servlet instance. + * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) + */ + @Override + protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) + throws Exception { + + this.servletInstance.service(request, response); + return null; + } + + + /** + * Destroy the wrapped Servlet instance. + * @see javax.servlet.Servlet#destroy() + */ + @Override + public void destroy() { + this.servletInstance.destroy(); + } + + + /** + * Internal implementation of the ServletConfig interface, to be passed + * to the wrapped servlet. Delegates to ServletWrappingController fields + * and methods to provide init parameters and other environment info. + */ + private class DelegatingServletConfig implements ServletConfig { + + @Override + public String getServletName() { + return servletName; + } + + @Override + public ServletContext getServletContext() { + return ServletBeanWrappingController.this.getServletContext(); + } + + @Override + public String getInitParameter(String paramName) { + return initParameters.getProperty(paramName); + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Enumeration getInitParameterNames() { + Enumeration paramNames = initParameters.keys(); // returns Enumeration + return paramNames; + } + + } + + + public Servlet getServletInstance() { + return servletInstance; + } + + public void setServletInstance(Servlet servletInstance) { + this.servletInstance = servletInstance; + } + + public String getServletName() { + return servletName; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/ClassPathStringTemplateGroup.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/ClassPathStringTemplateGroup.java new file mode 100644 index 0000000..da29e48 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/ClassPathStringTemplateGroup.java @@ -0,0 +1,141 @@ +package eu.dnetlib.springutils.stringtemplate; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.antlr.stringtemplate.StringTemplate; +import org.antlr.stringtemplate.StringTemplateErrorListener; +import org.antlr.stringtemplate.StringTemplateGroup; + +/** + * This subclass overrides the getFileNameFromTemplateName() and getTemplateNameFromFileName() methods in + * org.antlr.stringtemplate.StringTemplateGroup in order to retrieve template files from the classpath. + */ + +public class ClassPathStringTemplateGroup extends StringTemplateGroup { + + /** + * apply this prefix to all templates. + */ + private String prefix = ""; + + public ClassPathStringTemplateGroup(final String arg0) { + super(arg0); + } + + public ClassPathStringTemplateGroup(final Reader arg0) { + super(arg0); + } + + public ClassPathStringTemplateGroup(final String arg0, final String arg1) { + super(arg0, arg1); + } + + public ClassPathStringTemplateGroup(final String arg0, @SuppressWarnings("rawtypes") final Class arg1) { + super(arg0, arg1); + } + + public ClassPathStringTemplateGroup(final Reader arg0, final StringTemplateErrorListener arg1) { + super(arg0, arg1); + } + + public ClassPathStringTemplateGroup(final Reader arg0, @SuppressWarnings("rawtypes") final Class arg1) { + super(arg0, arg1); + } + + public ClassPathStringTemplateGroup(final String arg0, final String arg1, @SuppressWarnings("rawtypes") final Class arg2) { + super(arg0, arg1, arg2); + } + + public ClassPathStringTemplateGroup(final Reader arg0, @SuppressWarnings("rawtypes") final Class arg1, final StringTemplateErrorListener arg2) { + super(arg0, arg1, arg2); + } + + public ClassPathStringTemplateGroup(final Reader arg0, @SuppressWarnings("rawtypes") final Class arg1, final StringTemplateErrorListener arg2, final StringTemplateGroup arg3) { + super(arg0, arg1, arg2, arg3); + } + + /** + * {@inheritDoc} + * + * @see org.antlr.stringtemplate.StringTemplateGroup#getFileNameFromTemplateName(java.lang.String) + * + * @param filename + * with relative path + * @returns templatename + * + */ + @Override + public String getFileNameFromTemplateName(final String fileName) { + return getPrefix() + super.getFileNameFromTemplateName(fileName); + } + + /** + * {@inheritDoc} + * + * @see org.antlr.stringtemplate.StringTemplateGroup#getTemplateNameFromFileName(java.lang.String) + */ + @Override + public String getTemplateNameFromFileName(final String fileName) { + if (fileName.startsWith(getPrefix())) + return super.getTemplateNameFromFileName(fileName.substring(getPrefix().length())); + else + return super.getTemplateNameFromFileName(fileName); + } + + /** + * transforms a package name in a path and sets it as prefix. + * + * @param packageName + * java package name + */ + public void setPackage(final String packageName) { + setPrefix("/" + packageName.replace('.', '/') + "/"); + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(final String prefix) { + this.prefix = prefix; + } + + /** + * Base implementation doesn't work during integration tests for an obscure problem in Classloader vs Class + * getResourceAsStream, so we rewrite the important stuff here. + * + * {@inheritDoc} + * + * @see org.antlr.stringtemplate.StringTemplateGroup#loadTemplateFromBeneathRootDirOrCLASSPATH(java.lang.String) + */ + @Override + protected StringTemplate loadTemplateFromBeneathRootDirOrCLASSPATH(final String fileName) { + + final String name = getTemplateNameFromFileName(fileName); + final InputStream input = getClass().getResourceAsStream(fileName); + if(input == null) + throw new IllegalArgumentException("resource " + fileName + " doesn't exist"); + + + BufferedReader breader = null; // NOPMD: needed in finally block + try { + breader = new BufferedReader(getInputStreamReader(input)); + return loadTemplate(name, breader); + } catch (IOException ioe) { + error("Problem reading template file: " + fileName, ioe); + } finally { + if (breader != null) { + try { + breader.close(); + } catch (IOException ioe2) { + error("Cannot close template file: " + fileName, ioe2); + } + } + } + return null; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/StringTemplateFactory.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/StringTemplateFactory.java new file mode 100644 index 0000000..b99ba67 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/StringTemplateFactory.java @@ -0,0 +1,63 @@ +package eu.dnetlib.springutils.stringtemplate; + +import java.io.StringWriter; + +import org.antlr.stringtemplate.StringTemplate; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.core.io.Resource; + +/** + * Construct a string template instance from a spring resource (classpath or filesystem). + * + * @author marko + * + */ +public class StringTemplateFactory implements FactoryBean { + + /** + * template source. + */ + private Resource template; + + /** + * {@inheritDoc} + * + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + @Override + public StringTemplate getObject() throws Exception { + StringWriter body = new StringWriter(); + IOUtils.copy(template.getInputStream(), body); + return new StringTemplate(body.toString()); + } + + /** + * {@inheritDoc} + * + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + @Override + public Class getObjectType() { + return StringTemplate.class; + } + + /** + * {@inheritDoc} + * + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + @Override + public boolean isSingleton() { + return false; + } + + public Resource getTemplate() { + return template; + } + + public void setTemplate(final Resource template) { + this.template = template; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/StringTemplateView.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/StringTemplateView.java new file mode 100644 index 0000000..2bac2df --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/StringTemplateView.java @@ -0,0 +1,167 @@ +package eu.dnetlib.springutils.stringtemplate; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.antlr.stringtemplate.AutoIndentWriter; +import org.antlr.stringtemplate.StringTemplate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.validation.Errors; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.web.servlet.support.BindStatus; +import org.springframework.web.servlet.support.RequestContext; +import org.springframework.web.servlet.view.AbstractUrlBasedView; + +/** + * View using the StringTemplate engine. + * + *

    + * Exposes the following JavaBean properties: + *

    + *
      + *
    • stringTemplateName
    • : The name of the stringTemplate to be wrapped. + *
    + */ +public class StringTemplateView extends AbstractUrlBasedView { + /** + * logger. + */ + private static final Log log = LogFactory.getLog(StringTemplateView.class); // NOPMD + + /** + * template. + */ + private StringTemplate stringTemplate; + + public void setStringTemplate(final StringTemplate value) { + stringTemplate = value; + } + + public StringTemplate getStringTemplate() { + return stringTemplate; + } + + /** + * Overridable method to expose attributes to the model. + * + * @param model + * model + */ + // NOPMD + protected void exposeToModel(@SuppressWarnings("rawtypes") final Map model) { + if (isThemed()) { + exposeTheme(model); + exposeErrors(model); + } + } + + /** + * dummy. + * + * @return false + */ + protected boolean isThemed() { + return false; + } + + /** + * get request context. + * + * @param model + * model + * @return request context + */ + private RequestContext getRequestContext(final Map model) { + // XXX assumes name + return (RequestContext) model.get("rc"); + } + + /** + * expose theme. + * + * @param model + * model + */ + @SuppressWarnings("unchecked") + private void exposeTheme(@SuppressWarnings("rawtypes") final Map model) { + final RequestContext context = getRequestContext(model); + + // XXX assumes name + model.put("cssFile", context.getThemeMessage("css")); + } + + /** + * expose errors. + * + * @param model + * model + */ + @SuppressWarnings("unchecked") + private void exposeErrors(@SuppressWarnings("rawtypes") final Map model) { + final RequestContext context = getRequestContext(model); + + // XXX assumes name + final Errors errors = context.getErrors("command"); + if (errors == null) + return; + + final Map errorMap = new HashMap(); + + for (ObjectError objError : errors.getAllErrors()) { + final FieldError error = (FieldError) objError; + final String field = error.getField(); + final String objectName = error.getObjectName(); + final BindStatus bind = context.getBindStatus(objectName + "." + field); + final String message = bind.getErrorMessage(); + errorMap.put(field, message); + } + // XXX check for collision + model.put("errors", errorMap); + } + + /** + * Process the model map by merging it with the StringTemplate template. Output is directed to the servlet response. + *

    + * This method can be overridden if custom behavior is needed. + * + * @param model + * model + * @param request + * request + * @param response + * response + * @throws Exception + * exception + */ + @Override + protected void renderMergedOutputModel(@SuppressWarnings("rawtypes") final Map model, final HttpServletRequest request, final HttpServletResponse response) throws Exception { // NOPMD + if(response.getContentType() == null) + response.setContentType(getContentType()); + + exposeToModel(model); + logModel(model); + + stringTemplate.setAttributes(model); + stringTemplate.write(new AutoIndentWriter(response.getWriter())); + } + + /** + * log the model. + * + * @param model + * model + */ + private void logModel(@SuppressWarnings("rawtypes") final Map model) { + if (log.isDebugEnabled()) { + log.debug("Environment contains:"); + for (Object key : model.keySet()) + log.debug("model[" + key + "] = " + model.get(key)); + log.debug(""); + } + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/StringTemplateViewResolver.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/StringTemplateViewResolver.java new file mode 100644 index 0000000..9c66b4c --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/stringtemplate/StringTemplateViewResolver.java @@ -0,0 +1,135 @@ +package eu.dnetlib.springutils.stringtemplate; + +import java.util.Locale; + +import org.antlr.stringtemplate.StringTemplateGroup; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.ApplicationContextException; +import org.springframework.web.servlet.view.AbstractUrlBasedView; +import org.springframework.web.servlet.view.UrlBasedViewResolver; + +/** + * Spring MVC view resolver based on string templates. You can parameterize this resolver with a java package name under + * the templates will be searched. + * + *

    + * requires a StringTemplateGroup bean + *

    + * + *

    + * This code is based to a public domain example taken from here + *

    + * + * @author marko + * @author Brian Lewis + * + */ +public class StringTemplateViewResolver extends UrlBasedViewResolver { + + /** + * template group. + */ + private StringTemplateGroup templateGroup; + + public void setTemplateGroup(final StringTemplateGroup value) { + templateGroup = value; + } + + public StringTemplateGroup getTemplateGroup() { + return templateGroup; + } + + /** + * Sets default viewClass to requiredViewClass. + * + * @see #setViewClass + * @see #requiredViewClass + */ + public StringTemplateViewResolver() { + setViewClass(requiredViewClass()); // NOPMD + // XXX bad but we have to make assumptions later + setRequestContextAttribute("rc"); + } + + /** + * Requires StringTemplateView. + * + * @see StringTemplateView + * @return view class + */ + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected Class requiredViewClass() { + return StringTemplateView.class; + } + + /** + * Invoked on startup. Looks for a single StringTemplateGroup bean to find the relevant StringTemplate for this + * factory. + */ + @Override + protected void initApplicationContext() { + super.initApplicationContext(); + + if (getTemplateGroup() == null) { + // No explicit StringTemplateGroup: try to autodetect one. + setTemplateGroup(autodetectMyStringTemplateGroup()); + } + } + + /** + * Autodetect a StringTemplateGroup via the ApplicationContext. Called if no explicit StringTemplateGroup has been + * specified. + * + * @return the StringTemplateGroup to use for StringTemplateViews + * @see #getApplicationContext + * @see #setMyStringTemplateGroup + */ + protected StringTemplateGroup autodetectMyStringTemplateGroup() { + try { + final StringTemplateGroup group = (StringTemplateGroup) BeanFactoryUtils.beanOfTypeIncludingAncestors(getApplicationContext(), + StringTemplateGroup.class, true, false); + return group; + } catch (NoSuchBeanDefinitionException ex) { + throw new ApplicationContextException("Must define a single StringTemplateGroup bean in this web application context" + + " (may be inherited). StringTemplateGroup is the usual implementation." + " This bean may be given any name.", ex); + } + } + + /** + * {@inheritDoc} + * + * @see org.springframework.web.servlet.view.UrlBasedViewResolver#buildView(java.lang.String) + */ + @Override + protected AbstractUrlBasedView buildView(final String viewName) throws Exception { // NOPMD + if (logger.isDebugEnabled()) + logger.debug("looking up by view name: '" + viewName + "' based in '" + getPrefix() + "'"); + + final StringTemplateView view = (StringTemplateView) super.buildView(getPrefix() + viewName); + + view.setStringTemplate(templateGroup.getInstanceOf(getPrefix() + viewName)); + + return view; + } + + @Override + protected boolean canHandle(String viewName, Locale locale) { + if(viewName.startsWith("redirect:")) + return true; + + try { + templateGroup.getInstanceOf(getPrefix() + viewName); + } catch (IllegalArgumentException e) { + return false; + } + + if (super.canHandle(viewName, locale)) + return true; + + return true; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/BeanTemplate.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/BeanTemplate.java new file mode 100644 index 0000000..75b50ce --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/BeanTemplate.java @@ -0,0 +1,38 @@ +package eu.dnetlib.springutils.template; + +import org.w3c.dom.Element; + +/** + * Stores a bean template definition. + * + * @author marko + * + */ +public class BeanTemplate { + /** + * template name. + */ + private String name; + + /** + * template definition element. + */ + private Element root; + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public Element getRoot() { + return root; + } + + public void setRoot(final Element root) { + this.root = root; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/BeanTemplateDao.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/BeanTemplateDao.java new file mode 100644 index 0000000..e477af9 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/BeanTemplateDao.java @@ -0,0 +1,12 @@ +package eu.dnetlib.springutils.template; + +import java.util.List; + +public interface BeanTemplateDao { + void registerTemplate(BeanTemplate template); + + BeanTemplate getTemplate(String name); + + void registerTemplateInstance(TemplateInstance instance); + List getInstancesForTemplate(BeanTemplate template); +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/BeanTemplateDaoImpl.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/BeanTemplateDaoImpl.java new file mode 100644 index 0000000..6436126 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/BeanTemplateDaoImpl.java @@ -0,0 +1,50 @@ +package eu.dnetlib.springutils.template; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * TODO: refactor: this is no more a pure dao! + * + * @author marko + * + */ +public class BeanTemplateDaoImpl implements BeanTemplateDao { + Map byName = new HashMap(); + + Map> instances = new HashMap>(); + + @Override + public BeanTemplate getTemplate(String name) { + return byName.get(name); + } + + @Override + public void registerTemplate(BeanTemplate template) { + byName.put(template.getName(), template); + for(TemplateInstance instance : getInstancesForTemplate(template)) + instance.instantiate(template); + } + + @Override + public List getInstancesForTemplate(BeanTemplate template) { + return getInstancesForTemplate(template.getName()); + } + + public List getInstancesForTemplate(String templateName) { + List list = instances.get(templateName); + if(list == null) + list = new ArrayList(); + return list; + } + + @Override + public void registerTemplateInstance(TemplateInstance instance) { + List list = getInstancesForTemplate(instance.getTemplateName()); + list.add(instance); + instances.put(instance.getTemplateName(), list); + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateDefineDefinitionParser.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateDefineDefinitionParser.java new file mode 100644 index 0000000..9ff4c69 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateDefineDefinitionParser.java @@ -0,0 +1,67 @@ +package eu.dnetlib.springutils.template; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * This bean definition parser allows you to define parameterized bean definition templates. + * + *
    + * 	<template:define name="testTemplate">
    + *    <bean name="dummyName" t:name="$beanName$"
    + *       class="eu.dnetlib.springutils.template.TestBean" p:address="$someValue$">
    + *    </bean>
    + *  </template:define>
    + * 
    + * + * you can put a template parameter in property values + * + * @author marko + * + */ +public class TemplateDefineDefinitionParser implements BeanDefinitionParser { + /** + * logger. + */ + private static final Log log = LogFactory.getLog(TemplateDefineDefinitionParser.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * allows registering new templates. + */ + private BeanTemplateDao templateDao; + + /** + * construct a new template:define definition parser. + * + * @param templateDao + * dao object + */ + public TemplateDefineDefinitionParser(final BeanTemplateDao templateDao) { + super(); + this.templateDao = templateDao; + } + + /** + * {@inheritDoc} + * + * @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, + * org.springframework.beans.factory.xml.ParserContext) + */ + @Override + public BeanDefinition parse(final Element element, final ParserContext parserContext) { + log.debug("parsing template DEFINE: " + element.getChildNodes()); + + BeanTemplate template = new BeanTemplate(); + template.setName(element.getAttribute("name")); + template.setRoot(element); + + templateDao.registerTemplate(template); + + return null; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateInstance.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateInstance.java new file mode 100644 index 0000000..dbd8465 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateInstance.java @@ -0,0 +1,163 @@ +package eu.dnetlib.springutils.template; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.antlr.stringtemplate.StringTemplate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +/** + * Represents a expandable template instantiation. Used to store all the necessary information for the instantiation + * while waiting a suitable template definition to be parsed. + * + * @author marko + * + */ +public class TemplateInstance { + /** + * logger. + */ + private static final Log log = LogFactory.getLog(TemplateInstance.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * property namespace. + */ + private static final String NAMESPACE_P = "http://www.springframework.org/schema/p"; + + /** + * template short namespace. + */ + static final String NAMESPACE_T = "http://dnetlib.eu/springbeans/t"; + + /** + * template instance spring definition element. + */ + private Element element; + + /** + * spring parser context. + */ + private ParserContext parserContext; + + /** + * register a template instantiation. + * + * @param element + * element + * @param parserContext + * parserContext + */ + public TemplateInstance(final Element element, final ParserContext parserContext) { + super(); + this.element = element; + this.parserContext = parserContext; + } + + public String getTemplateName() { + return element.getAttribute("name"); + } + + public void instantiate(BeanTemplate template) { + log.debug("instantiating template " + template.getName()); + + final BeanDefinitionParserDelegate delegate = parserContext.getDelegate(); + + List beans = (List) DomUtils.getChildElementsByTagName(template.getRoot(), "bean"); + log.debug("found child elements beans: " + beans); + + for (Element el : beans) { + Element cel = (Element) el.cloneNode(true); + + expandTemplateParameters(cel, element, template); + + BeanDefinitionHolder holder; + holder = delegate.parseBeanDefinitionElement(cel); + BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry()); + + // necessary because of http://jira.springframework.org/browse/SPR-2955 + delegate.decorateBeanDefinitionIfRequired(cel, holder); + } + + } + + private void expandTemplateParameters(Element el, Element instance, BeanTemplate template) { + NamedNodeMap attrs = el.getAttributes(); + final Map templateParameters = getTemplateParameters(instance, template); + + for (int i = 0; i < attrs.getLength(); i++) { + Node node = attrs.item(i); + Attr attr = (Attr) node; + + if (NAMESPACE_T.equals(attr.getNamespaceURI())) { + log.debug("found template attribute: " + attr.getLocalName()); + + StringTemplate stp = new StringTemplate(attr.getValue()); + stp.setArgumentContext(templateParameters); + + el.setAttribute(attr.getLocalName(), stp.toString()); + + } else if (NAMESPACE_P.equals(attr.getNamespaceURI())) { + log.debug("found potential template property: " + attr.getLocalName()); + + if (attr.getValue().contains("$")) { + StringTemplate stp = new StringTemplate(attr.getValue()); + stp.setArgumentContext(templateParameters); + + el.setAttributeNS(NAMESPACE_P, attr.getLocalName(), stp.toString()); + } + } + } + } + + private Map getTemplateParameters(Element instance, BeanTemplate template) { + Map map = new HashMap(); + + fillParameterMap(map, template.getRoot()); // default + fillParameterMap(map, instance); + + List params = (List) DomUtils.getChildElementsByTagName(instance, "parameter"); + for (Element param : params) + map.put(param.getAttribute("name"), param.getAttribute("value")); + + return map; + } + + private void fillParameterMap(Map map, Element element) { + NamedNodeMap attrs = element.getAttributes(); + + for (int i = 0; i < attrs.getLength(); i++) { + Node node = attrs.item(i); + Attr attr = (Attr) node; + if (NAMESPACE_T.equals(attr.getNamespaceURI())) + map.put(attr.getLocalName(), attr.getValue()); + } + } + + public Element getElement() { + return element; + } + + public void setElement(Element element) { + this.element = element; + } + + public ParserContext getParserContext() { + return parserContext; + } + + public void setParserContext(ParserContext parserContext) { + this.parserContext = parserContext; + } + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateInstanceDefinitionParser.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateInstanceDefinitionParser.java new file mode 100644 index 0000000..ce4a1b6 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateInstanceDefinitionParser.java @@ -0,0 +1,93 @@ +package eu.dnetlib.springutils.template; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * instantiate a bean template defined with <template:define /> + * + * Parameters can be passed with: + * + *
    + * 	<template:instance name="testTemplate" t:beanName="test" />
    + * 
    + * + * or with: + * + *
    + * <template:instance name="testTemplate">
    + * 	 <template:parameter name="beanName" value="test"/>
    + * 	</template:instance>
    + * 
    + * + * TODO: you can also pass bean references with "value-ref", "t:somename-ref" or inner bean declaration inside the + * template:parameter element. + * + * @author marko + * + */ +public class TemplateInstanceDefinitionParser implements BeanDefinitionParser { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(TemplateInstanceDefinitionParser.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * template dao. Allows to find template definitions. + */ + private BeanTemplateDao templateDao; + + /** + * construct a template instance definition parser. + * + * @param templateDao + * template dao instance + */ + public TemplateInstanceDefinitionParser(BeanTemplateDao templateDao) { + super(); + this.templateDao = templateDao; + } + + /** + * {@inheritDoc} + * + * @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, + * org.springframework.beans.factory.xml.ParserContext) + */ + @Override + public BeanDefinition parse(Element element, ParserContext parserContext) { + TemplateInstance instance = new TemplateInstance(element, parserContext); + + final String templateName = instance.getTemplateName(); + log.debug("parsing tempate INSTANCE: " + templateName); + + BeanTemplate template = templateDao.getTemplate(templateName); + log.debug("found template: " + template); + + if (template == null) + stageInstance(instance); + else + instance.instantiate(template); + + return null; + } + + /** + * put this instance expansion in a staging queue, so that when the associated template definition + * will be processed we can resume the instantiation. + * + * @param element + * @param parserContext + */ + private void stageInstance(TemplateInstance instance) { + templateDao.registerTemplateInstance(instance); + } + + + +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateNamespaceHandler.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateNamespaceHandler.java new file mode 100644 index 0000000..5d21bfe --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateNamespaceHandler.java @@ -0,0 +1,26 @@ +package eu.dnetlib.springutils.template; + +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + +/** + * declares all the definition parsers for elements declared in the template schema. + * + * @author marko + * + */ +public class TemplateNamespaceHandler extends NamespaceHandlerSupport { + /** + * {@inheritDoc} + * @see org.springframework.beans.factory.xml.NamespaceHandler#init() + */ + @Override + public void init() { + BeanTemplateDao dao = new BeanTemplateDaoImpl(); + + super.registerBeanDefinitionParser("define", new TemplateDefineDefinitionParser(dao)); + + super.registerBeanDefinitionParser("instance", new TemplateInstanceDefinitionParser(dao)); + + super.registerBeanDefinitionParser("value", new TemplateValueDefinitionParser()); + } +} diff --git a/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateValueDefinitionParser.java b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateValueDefinitionParser.java new file mode 100644 index 0000000..f6c1d60 --- /dev/null +++ b/dnet-core-components/src/main/java/eu/dnetlib/springutils/template/TemplateValueDefinitionParser.java @@ -0,0 +1,22 @@ +package eu.dnetlib.springutils.template; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * This class performs no operation, it's just needed because spring requires a definition parser for every xml element + * he finds. + * + * @author marko + * + */ +public class TemplateValueDefinitionParser implements BeanDefinitionParser { + + @Override + public BeanDefinition parse(Element arg0, ParserContext arg1) { + return null; + } + +} diff --git a/dnet-core-components/src/main/resources/META-INF/jax-ws-catalog.xml b/dnet-core-components/src/main/resources/META-INF/jax-ws-catalog.xml new file mode 100644 index 0000000..3753552 --- /dev/null +++ b/dnet-core-components/src/main/resources/META-INF/jax-ws-catalog.xml @@ -0,0 +1,3 @@ + + + diff --git a/dnet-core-components/src/main/resources/META-INF/spring.handlers b/dnet-core-components/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000..5ec1f9b --- /dev/null +++ b/dnet-core-components/src/main/resources/META-INF/spring.handlers @@ -0,0 +1,2 @@ +http\://dnetlib.eu/springbeans/condbean=eu.dnetlib.springutils.condbean.ConditionalBeanNamespaceHandler +http\://dnetlib.eu/springbeans/template=eu.dnetlib.springutils.template.TemplateNamespaceHandler diff --git a/dnet-core-components/src/main/resources/META-INF/spring.schemas b/dnet-core-components/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000..424cc1e --- /dev/null +++ b/dnet-core-components/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,2 @@ +http\://dnetlib.eu/springbeans/condbean.xsd=eu/dnetlib/springutils/condbean/condbean.xsd +http\://dnetlib.eu/springbeans/template.xsd=eu/dnetlib/springutils/template/template.xsd diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/applicationContext-defaultProperties.properties b/dnet-core-components/src/main/resources/eu/dnetlib/applicationContext-defaultProperties.properties new file mode 100644 index 0000000..d26426d --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/applicationContext-defaultProperties.properties @@ -0,0 +1 @@ +transport.soap.baseAddress = http://${container.hostname}:${container.port}/${container.context}/services diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/applicationContext-dnet-data-provision-rmi.xml b/dnet-core-components/src/main/resources/eu/dnetlib/applicationContext-dnet-data-provision-rmi.xml new file mode 100644 index 0000000..44b5322 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/applicationContext-dnet-data-provision-rmi.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/enabling/locators/applicationContext-locators.properties b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/locators/applicationContext-locators.properties new file mode 100644 index 0000000..8b12b35 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/locators/applicationContext-locators.properties @@ -0,0 +1 @@ +default.service.comparator = preferLocalRunningInstanceComparator diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/enabling/locators/applicationContext-locators.xml b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/locators/applicationContext-locators.xml new file mode 100644 index 0000000..03e38a9 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/locators/applicationContext-locators.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/enabling/soap/applicationContext-common-eprbuilders.xml b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/soap/applicationContext-common-eprbuilders.xml new file mode 100644 index 0000000..0ab63d1 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/soap/applicationContext-common-eprbuilders.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/enabling/soap/cxf/applicationContext-eprbuilders.properties b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/soap/cxf/applicationContext-eprbuilders.properties new file mode 100644 index 0000000..9f2588e --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/soap/cxf/applicationContext-eprbuilders.properties @@ -0,0 +1 @@ +transport.soap.force.local.address=false diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/applicationContext-tools.xml b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/applicationContext-tools.xml new file mode 100644 index 0000000..b2b1fc9 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/applicationContext-tools.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/ServiceProfileSchemaTemplate.st b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/ServiceProfileSchemaTemplate.st new file mode 100644 index 0000000..a6dfa74 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/ServiceProfileSchemaTemplate.st @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/ServiceProfileTemplate.st b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/ServiceProfileTemplate.st new file mode 100644 index 0000000..02f4d7b --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/ServiceProfileTemplate.st @@ -0,0 +1,42 @@ + + +
    + + + + + + + + + $protocols.keys:{k|}$ + +
    + + + + 0 + 0 + 0 + + $properties.keys:{k|}$ + + + + 0 + 0 + + + + + + 0 + 0 + + + + + + + +
    diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/applicationContext-registration.properties b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/applicationContext-registration.properties new file mode 100644 index 0000000..8d3f2c3 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/applicationContext-registration.properties @@ -0,0 +1 @@ +services.registration.disabled=false diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/applicationContext-registration.xml b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/applicationContext-registration.xml new file mode 100644 index 0000000..a1ea63a --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/enabling/tools/registration/applicationContext-registration.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/functionality/cql/parse/applicationContext-CqlTranslator.xml b/dnet-core-components/src/main/resources/eu/dnetlib/functionality/cql/parse/applicationContext-CqlTranslator.xml new file mode 100644 index 0000000..e2dca59 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/functionality/cql/parse/applicationContext-CqlTranslator.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/functionality/index/client/applicationContext-indexClient.properties b/dnet-core-components/src/main/resources/eu/dnetlib/functionality/index/client/applicationContext-indexClient.properties new file mode 100644 index 0000000..1a4c9c9 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/functionality/index/client/applicationContext-indexClient.properties @@ -0,0 +1,14 @@ +#service.index.solr.rank.enable = false +service.index.solr.returnEmptyFields = true + +service.index.solr.cloud.zkurl = quorum1.t.hadoop.research-infrastructures.eu:2182,quorum2.t.hadoop.research-infrastructures.eu:2182,quorum3.t.hadoop.research-infrastructures.eu:2182/solr +service.index.solr.cloud.url.list = http://node[2..13].t.hadoop.research-infrastructures.eu:8501/solr +service.index.solr.cloud.url.local = http://localhost:8501/solr +service.index.solr.highlight.utils = eu.dnetlib.functionality.index.solr.utils.HighlightUtils + + +services.mapreduce.index.solr.shutdown.wait = 30000 +services.mapreduce.index.solr.buffer.threshold = 10 +services.mapreduce.index.solr.local.feeding = true + +service.solr.index.client.jsonConfiguration= {"id":"solr"} diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/functionality/index/client/applicationContext-indexClient.xml b/dnet-core-components/src/main/resources/eu/dnetlib/functionality/index/client/applicationContext-indexClient.xml new file mode 100644 index 0000000..16077fd --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/functionality/index/client/applicationContext-indexClient.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/soap/cxf/applicationContext-eprbuilders.xml b/dnet-core-components/src/main/resources/eu/dnetlib/soap/cxf/applicationContext-eprbuilders.xml new file mode 100644 index 0000000..ff122cb --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/soap/cxf/applicationContext-eprbuilders.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/springutils/condbean/applicationContext-condbean.xml b/dnet-core-components/src/main/resources/eu/dnetlib/springutils/condbean/applicationContext-condbean.xml new file mode 100644 index 0000000..afe13a3 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/springutils/condbean/applicationContext-condbean.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/springutils/condbean/condbean.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/springutils/condbean/condbean.xsd new file mode 100644 index 0000000..1cec10a --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/springutils/condbean/condbean.xsd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/springutils/condbean/parser/CondBeanParser.syntax b/dnet-core-components/src/main/resources/eu/dnetlib/springutils/condbean/parser/CondBeanParser.syntax new file mode 100644 index 0000000..747cbaa --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/springutils/condbean/parser/CondBeanParser.syntax @@ -0,0 +1,101 @@ +///////////////////////////// +// CondBean parser syntax // +//////////////////////////// + + +conditionalBean ::= expression + ; + +expression ::= assignmentExpression + ; + +assignmentExpression ::= conditionalExpression + ; + +conditionalExpression ::= logicalOrExpression + ; + +logicalOrExpression ::= + logicalAndExpression + | logicalOrExpression orOperator logicalAndExpression + ; + +logicalAndExpression ::= + equalityExpression + | logicalAndExpression andOperator equalityExpression + ; + +equalityExpression ::= + relationalExpression + | equalityExpression "==" relationalExpression + | equalityExpression "!=" relationalExpression + ; + +relationalExpression ::= + unaryExpression + | relationalExpression '<' unaryExpression + | relationalExpression '>' unaryExpression + | relationalExpression "<=" unaryExpression + | relationalExpression ">=" unaryExpression + ; + +unaryExpression ::= + '!' unaryExpression + | "${" qualifiedType '}' + | primaryExpression + | "isdef" '(' simpleType ')' + ; + +primaryExpression ::= + literal + | '(' expression ')' + ; + + +orOperator ::= "||" | "or" ; +andOperator ::= "&&" | "and" ; + +token ::= qualifiedName ; +token ::= simpleName; +token ::= orOperator; +token ::= andOperator; +token ::= chars; +token ::= altStringLiteral; + +literal ::= + numberType + | booleanType + | stringType + ; + +simpleName ::= Identifier + ; +qualifiedName ::= + Identifier (dot Identifier)+ + ; + + + +Identifier ::= `identifier`; + + +booleanType ::= booleanLiteral ; +numberType ::= integerLiteral | floatingPointLiteral ; +stringType ::= stringLiteral | altStringLiteral ; +qualifiedType ::= qualifiedName; +simpleType ::= simpleName; + + +booleanLiteral ::= "true" | "false" ; + +integerLiteral ::= `integer` ; +floatingPointLiteral ::= `float` ; +stringLiteral ::= `stringdef` ; +altStringLiteral ::= quote chars quote ; + +chars ::= (`char` - "'")* ; + +dot ::= '.' ; +quote ::= "'" ; + +ignored ::= `whitespaces` ; \ No newline at end of file diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/springutils/template/template.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/springutils/template/template.xsd new file mode 100644 index 0000000..f165ef5 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/springutils/template/template.xsd @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ActionManagerSetDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ActionManagerSetDSResourceType.xsd new file mode 100644 index 0000000..200d45d --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ActionManagerSetDSResourceType.xsd @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/AnnouncementDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/AnnouncementDSResourceType.xsd new file mode 100644 index 0000000..0d34e04 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/AnnouncementDSResourceType.xsd @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CitationDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CitationDSResourceType.xsd new file mode 100644 index 0000000..2f01205 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CitationDSResourceType.xsd @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CleanerDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CleanerDSResourceType.xsd new file mode 100644 index 0000000..36b9bf1 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CleanerDSResourceType.xsd @@ -0,0 +1,86 @@ + + + + + Standard part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Customisable part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CollectionDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CollectionDSResourceType.xsd new file mode 100644 index 0000000..5a0150d --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CollectionDSResourceType.xsd @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CommunityDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CommunityDSResourceType.xsd new file mode 100644 index 0000000..0b0eeae --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/CommunityDSResourceType.xsd @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ContextDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ContextDSResourceType.xsd new file mode 100644 index 0000000..71de0a3 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ContextDSResourceType.xsd @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/DedupConfigurationDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/DedupConfigurationDSResourceType.xsd new file mode 100644 index 0000000..abc005d --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/DedupConfigurationDSResourceType.xsd @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/DedupOrchestrationDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/DedupOrchestrationDSResourceType.xsd new file mode 100644 index 0000000..fedabdc --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/DedupOrchestrationDSResourceType.xsd @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/EntityGrouperConfigurationDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/EntityGrouperConfigurationDSResourceType.xsd new file mode 100644 index 0000000..3bbc22d --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/EntityGrouperConfigurationDSResourceType.xsd @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/FeatureExtractionDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/FeatureExtractionDSResourceType.xsd new file mode 100644 index 0000000..234ff69 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/FeatureExtractionDSResourceType.xsd @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/GroovyProcessingDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/GroovyProcessingDSResourceType.xsd new file mode 100644 index 0000000..01622d6 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/GroovyProcessingDSResourceType.xsd @@ -0,0 +1,78 @@ + + + + Standard part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Customisable part of any Resource Profile + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/HadoopJobConfigurationDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/HadoopJobConfigurationDSResourceType.xsd new file mode 100644 index 0000000..ff68d55 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/HadoopJobConfigurationDSResourceType.xsd @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/IndexDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/IndexDSResourceType.xsd new file mode 100644 index 0000000..512ebf8 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/IndexDSResourceType.xsd @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/InformationSpaceViewDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/InformationSpaceViewDSResourceType.xsd new file mode 100644 index 0000000..5543e3f --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/InformationSpaceViewDSResourceType.xsd @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/MDFormatDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/MDFormatDSResourceType.xsd new file mode 100644 index 0000000..0ef6cf5 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/MDFormatDSResourceType.xsd @@ -0,0 +1,145 @@ + + + + Standard part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Customisable part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/MDStoreDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/MDStoreDSResourceType.xsd new file mode 100644 index 0000000..114b4b9 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/MDStoreDSResourceType.xsd @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/MetadataFormatDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/MetadataFormatDSResourceType.xsd new file mode 100644 index 0000000..99948b3 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/MetadataFormatDSResourceType.xsd @@ -0,0 +1,96 @@ + + + + + Standard part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Customisable part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/OAIPublisherConfigurationDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/OAIPublisherConfigurationDSResourceType.xsd new file mode 100644 index 0000000..9a131d3 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/OAIPublisherConfigurationDSResourceType.xsd @@ -0,0 +1,207 @@ + + + Standard part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Customisable part of any Resource Profile + + + + + + + + + + + + + + + + ENRICHMENT: xpaths to identify when a record has + been enriched + and must thus be inserted into a special + "setSpec_enriched" + + + + + + + + + + + + + + + + + + + + + Definition of the OAI sets to export. +
      +
    • element spec: the setSpec.
    • +
    • element name: the name of the set
    • +
    • element description: description of the set
    • +
    • element query: records in this set are results of this query
    • +
    • attribute enabled: is the OAI set to export? Default is true
    • +
    +
    +
    + + + + + + + +
    + + + + + + + + +
      +
    • @exportable: true if we want to export + the metadata format via + OAI-PMH. Default is false.
    • +
    • @metadataPrefix: metadata prefix to be used for + OAI-PMH call. + E.g., + oai_dc
    • +
    • NAMESPACE: target metadata format + namespace (xmlns). E.g., + http://www.openarchives.org/OAI/2.0/oai_dc/
    • +
    • +
    • SCHEMA: URL to the XML Schema. E.g., + http://www.openarchives.org/OAI/2.0/oai_dc.xsd
    • +
    • SOURCE_METADATA_FORMAT/@name: metadata format of the mdstores + to be used as sources
    • +
    • SOURCE_METADATA_FORMAT/@interpretation: interpretation of the + mdstores to be used as sources
    • +
    • SOURCE_METADATA_FORMAT/@layout: layout of the mdstores to be + used as sources
    • +
    • TRANSFORMATION_RULE: identifier of the transformation to apply + to source records and transform them into the target + format
    • +
    • BASE_QUERY: base query to apply to filter records to export
    • +
    +
    +
    + + + + + + + + + + + + + + + +
    + + + + One INDEX element represents one index to be created in the + PublisherStore. + When defining one index, you need to specify where to + find the value to + be stored in that indexed field for each source + metadata formats. +
      +
    • SOURCE: information to identify one source metadata format
    • +
    • SOURCE/@path: xpath where to find the value to be used in the + indexed field.
    • +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ObjectStoreDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ObjectStoreDSResourceType.xsd new file mode 100644 index 0000000..e503dd3 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ObjectStoreDSResourceType.xsd @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/OntologyDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/OntologyDSResourceType.xsd new file mode 100644 index 0000000..0da1cce --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/OntologyDSResourceType.xsd @@ -0,0 +1,117 @@ + + + Standard part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Customisable part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/QueryHashDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/QueryHashDSResourceType.xsd new file mode 100644 index 0000000..78978e3 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/QueryHashDSResourceType.xsd @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RSSFeedDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RSSFeedDSResourceType.xsd new file mode 100644 index 0000000..0d876c5 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RSSFeedDSResourceType.xsd @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RecommendationDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RecommendationDSResourceType.xsd new file mode 100644 index 0000000..ce09b90 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RecommendationDSResourceType.xsd @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RegionDescriptionDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RegionDescriptionDSResourceType.xsd new file mode 100644 index 0000000..2bacb4a --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RegionDescriptionDSResourceType.xsd @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RepositoryServiceResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RepositoryServiceResourceType.xsd new file mode 100644 index 0000000..03a4a7e --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/RepositoryServiceResourceType.xsd @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Type for the datasource original identifier. + The + original id is not the D-Net profile identifier, but an id that + identifies the datasource itself in the outside world. + The provenance + attribute references to the organisation,institution, + etc, where the + id had been taken from. + For example, the profile for a repository + that is in OpenDoar will have + its opendoar id with provenance + 'OpenDoar'. + This field should be used instead of the old schema + EXTRA_FIELD with key='OpenAireDataSourceId' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The list of environments that can manage this + datasource. + An environment identifies a set of services of one or + more D-Net + installations. + The element ENVIRONMENT supercedes the + EXTRA_FIELD element with + key='aggregatorName'. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SecurityContextDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SecurityContextDSResourceType.xsd new file mode 100644 index 0000000..c1bb634 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SecurityContextDSResourceType.xsd @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SecurityPolicyDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SecurityPolicyDSResourceType.xsd new file mode 100644 index 0000000..17cd8db --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SecurityPolicyDSResourceType.xsd @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SecurityProfileDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SecurityProfileDSResourceType.xsd new file mode 100644 index 0000000..c8f5ad3 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SecurityProfileDSResourceType.xsd @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SimilarityDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SimilarityDSResourceType.xsd new file mode 100644 index 0000000..dac23c9 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SimilarityDSResourceType.xsd @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/StoreDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/StoreDSResourceType.xsd new file mode 100644 index 0000000..3327c51 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/StoreDSResourceType.xsd @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SystemManagementDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SystemManagementDSResourceType.xsd new file mode 100644 index 0000000..ba009a8 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/SystemManagementDSResourceType.xsd @@ -0,0 +1,149 @@ + + + Standard part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Customisable part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/TransformationRuleDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/TransformationRuleDSResourceType.xsd new file mode 100644 index 0000000..0c11bdd --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/TransformationRuleDSResourceType.xsd @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/UserDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/UserDSResourceType.xsd new file mode 100644 index 0000000..21ec327 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/UserDSResourceType.xsd @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ValidatorDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ValidatorDSResourceType.xsd new file mode 100644 index 0000000..7dc2e3d --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/ValidatorDSResourceType.xsd @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/VocabularyDSResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/VocabularyDSResourceType.xsd new file mode 100644 index 0000000..faabaf7 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/VocabularyDSResourceType.xsd @@ -0,0 +1,117 @@ + + + Standard part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Customisable part of any Resource Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/WebInterfaceLayoutResourceType.xsd b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/WebInterfaceLayoutResourceType.xsd new file mode 100644 index 0000000..af84808 --- /dev/null +++ b/dnet-core-components/src/main/resources/eu/dnetlib/test/schemas/WebInterfaceLayoutResourceType.xsd @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-components/src/test/java/eu/dnetlib/cxf/CxfEndpointReferenceBuilderTest.java b/dnet-core-components/src/test/java/eu/dnetlib/cxf/CxfEndpointReferenceBuilderTest.java new file mode 100644 index 0000000..28ad232 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/cxf/CxfEndpointReferenceBuilderTest.java @@ -0,0 +1,126 @@ +package eu.dnetlib.soap.cxf; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import java.util.HashMap; + +import javax.xml.namespace.QName; +import javax.xml.ws.EndpointReference; + +import org.apache.cxf.endpoint.Endpoint; +import org.apache.cxf.service.Service; +import org.apache.cxf.service.model.EndpointInfo; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * test the CxfEndpointReferenceBuilder. + * + * @author marko + * + */ +@RunWith(MockitoJUnitRunner.class) +public class CxfEndpointReferenceBuilderTest { + + /** + * service mock. + */ + @Mock + private Service service; + + /** + * endpoint info mock. + */ + @Mock + private EndpointInfo endpointInfo; + + /** + * endpoint mock. + */ + @Mock + private Endpoint endpoint; + + /** + * object under test. + */ + private CxfEndpointReferenceBuilder builder; + + /** + * initialize object under test and prepare a commmon endpoint mock. + */ + @Before + public void setUp() { + builder = new CxfEndpointReferenceBuilder(); + when(service.getName()).thenReturn(new QName("http://my.test", "TestService")); + + when(endpoint.getEndpointInfo()).thenReturn(endpointInfo); + when(endpoint.getService()).thenReturn(service); + when(endpointInfo.getAddress()).thenReturn("http://localhost/something"); + when(endpointInfo.getName()).thenReturn(new QName("http://my.test", "TestServiceEndpoint")); + } + + /** + * test. + */ + @Test + public void testGetEndpointReference() { + + final EndpointReference epr = builder.getEndpointReference(endpoint); + assertTrue("check serialization", epr.toString().contains("TestServiceEndpoint")); + assertTrue("check serialization", epr.toString().contains("http://localhost/something")); + assertTrue("check serialization", epr.toString().contains("http://my.test")); + + } + + /** + * test method. + */ + @Test + public void testGetEndpointReferenceMetadata() { + final HashMap defaultMetadata = new HashMap(); + defaultMetadata.put("{http://someuri}somename", "somevalue"); + builder.setDefaultMetadata(defaultMetadata); + + final EndpointReference epr = builder.getEndpointReference(endpoint); + + assertTrue("check serialization", epr.toString().contains("somename xmlns=\"http://someuri\"")); + assertTrue("check serialization", epr.toString().contains("xmlns=\"http://someuri\"")); + assertTrue("check serialization", epr.toString().contains("somevalue")); + } + + protected Service getService() { + return service; + } + + protected void setService(final Service service) { + this.service = service; + } + + protected EndpointInfo getEndpointInfo() { + return endpointInfo; + } + + protected void setEndpointInfo(final EndpointInfo endpointInfo) { + this.endpointInfo = endpointInfo; + } + + protected Endpoint getEndpoint() { + return endpoint; + } + + protected void setEndpoint(final Endpoint endpoint) { + this.endpoint = endpoint; + } + + protected CxfEndpointReferenceBuilder getBuilder() { + return builder; + } + + protected void setBuilder(final CxfEndpointReferenceBuilder builder) { + this.builder = builder; + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/cxf/StandaloneCxfEndpointReferenceBuilderTest.java b/dnet-core-components/src/test/java/eu/dnetlib/cxf/StandaloneCxfEndpointReferenceBuilderTest.java new file mode 100644 index 0000000..a13abf3 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/cxf/StandaloneCxfEndpointReferenceBuilderTest.java @@ -0,0 +1,74 @@ +package eu.dnetlib.soap.cxf; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * test StandaloneCxfEndpointReferenceBuilderTest. + * + * @author marko + * + */ +@RunWith(MockitoJUnitRunner.class) +public class StandaloneCxfEndpointReferenceBuilderTest extends CxfEndpointReferenceBuilderTest { + + /** + * some constant url prefix. + */ + private static final String HTTP_TEST_COM = "http://test.com"; + + /** + * class under test. + */ + private transient StandaloneCxfEndpointReferenceBuilder builder; + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.soap.cxf.CxfEndpointReferenceBuilderTest#setUp() + */ + @Override + @Before + public void setUp() { + super.setUp(); + builder = new StandaloneCxfEndpointReferenceBuilder(); + } + + /** + * test computeAddress. + */ + @Test + public void testGetAddress() { + assertEquals("no base, http", "http://localhost/something", builder.getAddress(getEndpoint())); + + when(getEndpointInfo().getAddress()).thenReturn("/localPath"); + assertEquals("no base, no http", "/localPath", builder.getAddress(getEndpoint())); + + builder.setBaseAddress("http://somebase"); + assertEquals("base, no http", "http://somebase/localPath", builder.getAddress(getEndpoint())); + } + + /** + * make code coverage happy. + */ + @Test + public void testGetBaseAddress() { + builder.setBaseAddress(HTTP_TEST_COM); + assertEquals("testing setter", HTTP_TEST_COM, builder.getBaseAddress()); + } + + /** + * make code coverage happy. + */ + @Test + public void testSetBaseAddress() { + builder.setBaseAddress(HTTP_TEST_COM); + assertEquals("testing setter", HTTP_TEST_COM, builder.getBaseAddress()); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/DefaultUniqueServiceLocatorTest.java b/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/DefaultUniqueServiceLocatorTest.java new file mode 100644 index 0000000..9a0d41b --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/DefaultUniqueServiceLocatorTest.java @@ -0,0 +1,154 @@ +package eu.dnetlib.enabling.locators; + +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.locators.comparators.PreferLocalRunningInstanceComparator; +import eu.dnetlib.enabling.resultset.rmi.ResultSetService; +import eu.dnetlib.enabling.tools.OpaqueResource; +import eu.dnetlib.enabling.tools.registration.ServiceNameResolver; +import eu.dnetlib.enabling.tools.registration.ServiceRegistrationManager; +import org.antlr.stringtemplate.StringTemplate; +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.context.ApplicationContext; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class DefaultUniqueServiceLocatorTest { + + /** + * Class under test. + */ + private DefaultUniqueServiceLocator locator; + + @Mock + private ISLookUpService lookupService; + @Mock + private ResultSetService resultsetService; + @Mock + private ApplicationContext appContext; + @Mock + private ServiceNameResolver serviceNameResolver; + @Mock + private ServiceRegistrationManager resultSetRegistrator; + @Mock + private OpaqueResource opaqueResource; + + private static final String SERVICE_NAME = "ResultsetService"; + + private static final String XQUERY = "for $x in collection('/db/DRIVER/ServiceResources/" + SERVICE_NAME + "ResourceType') return $x"; + + @Before + public void setUp() throws Exception { + locator = new DefaultUniqueServiceLocator(); + locator.setApplicationContext(appContext); + locator.setDefaultComparator(new PreferLocalRunningInstanceComparator()); + locator.setServiceNameResolver(serviceNameResolver); + locator.setIsLookupService(lookupService); + + final Map registratorBeans = Maps.newHashMap(); + registratorBeans.put("resultSetRegistrator", resultSetRegistrator); + final Map serviceBeans = Maps.newHashMap(); + serviceBeans.put("resultSetService", resultsetService); + + final StringTemplate st1 = new StringTemplate(IOUtils.toString(getClass().getResourceAsStream("serviceProfile.xml.st"))); + st1.setAttribute("name", SERVICE_NAME); + st1.setAttribute("id", "1111"); + final StringTemplate st2 = new StringTemplate(IOUtils.toString(getClass().getResourceAsStream("serviceProfile.xml.st"))); + st2.setAttribute("name", SERVICE_NAME); + st2.setAttribute("id", "2222"); + final StringTemplate st3 = new StringTemplate(IOUtils.toString(getClass().getResourceAsStream("serviceProfile.xml.st"))); + st3.setAttribute("name", SERVICE_NAME); + st3.setAttribute("id", "3333"); + + when(serviceNameResolver.getName(ResultSetService.class)).thenReturn(SERVICE_NAME); + when(lookupService.quickSearchProfile(XQUERY)).thenReturn(Lists.newArrayList(st1.toString(), st2.toString(), st3.toString())); + when(lookupService.getResourceProfileByQuery(anyString())).thenReturn(st2.toString()); + //when(appContext.getBeansOfType(ServiceRegistrationManager.class)).thenReturn(registratorBeans); + //when(appContext.getBeansOfType(ResultSetService.class)).thenReturn(serviceBeans); + + //when(resultSetRegistrator.getServiceProfile()).thenReturn(opaqueResource); + //when(resultSetRegistrator.getService()).thenReturn(resultsetService); + //when(opaqueResource.getResourceId()).thenReturn("2222"); + + } + + @Test + public void testGetServiceClassOfT_1() { + assertEquals(lookupService, locator.getService(ISLookUpService.class)); + } + + @Test + @Ignore + public void testGetServiceClassOfT_2() throws ISLookUpException { + assertEquals(resultsetService, locator.getService(ResultSetService.class, false)); + verify(lookupService, times(1)).quickSearchProfile(XQUERY); + } + + @Test + @Ignore + public void testGetServiceClassOfTComparatorOfServiceRunningInstance() throws ISLookUpException { + assertEquals(resultsetService, locator.getService(ResultSetService.class, new PreferLocalRunningInstanceComparator())); + verify(lookupService, times(1)).quickSearchProfile(XQUERY); + } + + @Test + @Ignore + public void testGetServiceClassOfTString() { + assertEquals(resultsetService, locator.getService(ResultSetService.class, "rs-1234")); + } + + @Test + @Ignore + public void testGetServiceClassOfTBoolean() throws ISLookUpException { + assertEquals(resultsetService, locator.getService(ResultSetService.class, true)); + verify(lookupService, never()).quickSearchProfile(XQUERY); + } + + @Test + public void testGetServiceIdClassOfT() { + assertEquals("1111", locator.getServiceId(ResultSetService.class)); + } + + @Test + public void testGetServiceIdClassOfTComparatorOfServiceRunningInstance() { + assertEquals("1111", locator.getServiceId(ResultSetService.class, new PreferLocalRunningInstanceComparator())); + } + + @Test + public void testGetServiceIdClassOfTString() { + assertEquals("2222", locator.getServiceId(ResultSetService.class, "rs-1234")); + } + + @Test + @Ignore + public void testGetAllServices() { + final Set list = locator.getAllServices(ResultSetService.class); + assertEquals(3, list.size()); + assertTrue(list.contains(resultsetService)); + } + + @Test + public void testGetAllServiceIds() { + final Set list = locator.getAllServiceIds(ResultSetService.class); + assertEquals(3, list.size()); + assertTrue(list.contains("1111")); + assertTrue(list.contains("2222")); + assertTrue(list.contains("3333")); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/comparators/DiskSpaceComparatorTest.java b/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/comparators/DiskSpaceComparatorTest.java new file mode 100644 index 0000000..40093af --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/comparators/DiskSpaceComparatorTest.java @@ -0,0 +1,45 @@ +package eu.dnetlib.enabling.locators.comparators; + +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.*; + +import org.mockito.junit.MockitoJUnitRunner; + +import eu.dnetlib.enabling.locators.ServiceRunningInstance; + +@RunWith(MockitoJUnitRunner.class) +public class DiskSpaceComparatorTest { + + /** + * Class Under test. + */ + private DiskSpaceComparator comparator; + + @Mock + private ServiceRunningInstance s1; + @Mock + private ServiceRunningInstance s2; + @Mock + private ServiceRunningInstance s3; + + @Before + public void setUp() throws Exception { + comparator = new DiskSpaceComparator(); + when(s1.getUsedDiskSpace()).thenReturn(0); + when(s2.getUsedDiskSpace()).thenReturn(0); + when(s3.getUsedDiskSpace()).thenReturn(10); + } + + @Test + public void testCompare() { + assertEquals(0, comparator.compare(s1, s2)); + assertEquals(-1, comparator.compare(s1, s3)); + assertEquals(1, comparator.compare(s3, s1)); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/comparators/HandledDatastructuresComparatorTest.java b/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/comparators/HandledDatastructuresComparatorTest.java new file mode 100644 index 0000000..a3938bf --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/comparators/HandledDatastructuresComparatorTest.java @@ -0,0 +1,44 @@ +package eu.dnetlib.enabling.locators.comparators; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import eu.dnetlib.enabling.locators.ServiceRunningInstance; + +@RunWith(MockitoJUnitRunner.class) +public class HandledDatastructuresComparatorTest { + + /** + * Class Under test. + */ + private HandledDatastructuresComparator comparator; + + @Mock + private ServiceRunningInstance s1; + @Mock + private ServiceRunningInstance s2; + @Mock + private ServiceRunningInstance s3; + + @Before + public void setUp() throws Exception { + comparator = new HandledDatastructuresComparator(); + when(s1.getHandledDatastructures()).thenReturn(0); + when(s2.getHandledDatastructures()).thenReturn(0); + when(s3.getHandledDatastructures()).thenReturn(10); + } + + @Test + public void testCompare() { + assertEquals(0, comparator.compare(s1, s2)); + assertEquals(-1, comparator.compare(s1, s3)); + assertEquals(1, comparator.compare(s3, s1)); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/comparators/PreferLocalRunningInstanceComparatorTest.java b/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/comparators/PreferLocalRunningInstanceComparatorTest.java new file mode 100644 index 0000000..3845951 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/enabling/locators/comparators/PreferLocalRunningInstanceComparatorTest.java @@ -0,0 +1,44 @@ +package eu.dnetlib.enabling.locators.comparators; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import eu.dnetlib.enabling.locators.ServiceRunningInstance; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class PreferLocalRunningInstanceComparatorTest { + + /** + * Class Under test. + */ + private PreferLocalRunningInstanceComparator comparator; + + @Mock + private ServiceRunningInstance s1; + @Mock + private ServiceRunningInstance s2; + @Mock + private ServiceRunningInstance s3; + + @Before + public void setUp() throws Exception { + comparator = new PreferLocalRunningInstanceComparator(); + when(s1.isLocal()).thenReturn(true); + when(s2.isLocal()).thenReturn(false); + when(s3.isLocal()).thenReturn(false); + } + + @Test + public void testCompare() { + assertEquals(-1, comparator.compare(s1, s3)); + assertEquals(1, comparator.compare(s3, s1)); + assertEquals(0, comparator.compare(s2, s3)); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/enabling/tools/DynamicServiceEnumeratorTest.java b/dnet-core-components/src/test/java/eu/dnetlib/enabling/tools/DynamicServiceEnumeratorTest.java new file mode 100644 index 0000000..b86a1ba --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/enabling/tools/DynamicServiceEnumeratorTest.java @@ -0,0 +1,112 @@ +package eu.dnetlib.enabling.tools; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.tools.registration.ServiceNameResolver; +import eu.dnetlib.soap.cxf.StandaloneCxfEndpointReferenceBuilder; + +/** + * Test the dynamic service enumerator. + * + * @author marko + * + */ +@RunWith(MockitoJUnitRunner.class) +public class DynamicServiceEnumeratorTest { + + /** + * Dummy service interface. + * + * @author marko + * + */ + interface TestService { + } + + /** + * instance under test. + */ + private transient DynamicServiceEnumerator enumerator; + + /** + * lookup service locator mock + */ + @Mock + private transient ISLookUpService lookUpService; + + /** + * lookup mock locator. + */ + private transient ServiceLocator lookUpLocator; + + /** + * service name resolver mock. + */ + @Mock + private transient ServiceNameResolver nameResolver; + + /** + * epr builder. + */ + StandaloneCxfEndpointReferenceBuilder eprBuilder; + + /** + * common preparation. + */ + @Before + public void setUp() { + lookUpLocator = new StaticServiceLocator(lookUpService); + eprBuilder = new StandaloneCxfEndpointReferenceBuilder(); + + enumerator = new DynamicServiceEnumerator(TestService.class); + enumerator.setLookUpLocator(lookUpLocator); + enumerator.setServiceNameResolver(nameResolver); + enumerator.setEprBuilder(eprBuilder); + + when(nameResolver.getName(TestService.class)).thenReturn("TestService"); + } + + /** + * test get services. + * + * @throws ISLookUpException mock exception + */ + @Test + public void testGetServices() throws ISLookUpException { + List services = new ArrayList(); + services.add("123http://test.com"); + + when(lookUpService.quickSearchProfile(anyString())).thenReturn(services); + + List> instances = enumerator.getServices(); + + assertEquals("query size", 1, instances.size()); + assertEquals("service id", "123", instances.get(0).getServiceId()); + assertEquals("service url", "http://test.com", instances.get(0).getUrl()); + assertNotNull("service epr", instances.get(0).getEpr()); + } + + @Test + public void testEmptyGetServices() throws ISLookUpException { + List services = new ArrayList(); + when(lookUpService.quickSearchProfile(anyString())).thenReturn(services); + + List> instances = enumerator.getServices(); + assertTrue("empty", instances.isEmpty()); + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/enabling/tools/DynamicServiceLocatorTest.java b/dnet-core-components/src/test/java/eu/dnetlib/enabling/tools/DynamicServiceLocatorTest.java new file mode 100644 index 0000000..645f62e --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/enabling/tools/DynamicServiceLocatorTest.java @@ -0,0 +1,77 @@ +package eu.dnetlib.enabling.tools; + +import static org.junit.Assert.*; // NOPMD +import static org.mockito.Mockito.*; // NOPMD + +import java.util.List; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException; +import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService; +import eu.dnetlib.enabling.resultset.rmi.ResultSetService; + +/** + * Test dynamic service locator. + * + * @author marko + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class DynamicServiceLocatorTest { + + /** + * instance under test. + */ + @Autowired + private transient DynamicServiceLocator serviceLocator; + + /** + * list of mock service profiles saved in files. + */ + + @javax.annotation.Resource(name = "serviceProfiles") + private transient List serviceUris; + + /** + * service resolver mock. + */ + private transient ServiceResolver serviceResolver; + + /** + * setup. + * + * @throws ISLookUpException + * mock + */ + @Before + public void setUp() throws ISLookUpException { +// final ISLookUpService lookupService = mock(ISLookUpService.class); +// serviceLocator.setLookUpLocator(new StaticServiceLocator(lookupService)); + + serviceResolver = mock(ServiceResolver.class); + serviceLocator.setServiceResolver(serviceResolver); + + final ISLookUpService lookupService = serviceLocator.getLookUpLocator().getService(); + when(lookupService.quickSearchProfile(anyString())).thenReturn(serviceUris); + } + + /** + * test get service. + */ + @Test + public void testGetService() { + assertNull("dummy", serviceLocator.getService()); + + verify(serviceResolver).getService(eq(ResultSetService.class), (W3CEndpointReference) anyObject()); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/enabling/tools/registration/InterfaceServiceNameResolverCompatibilityTest.java b/dnet-core-components/src/test/java/eu/dnetlib/enabling/tools/registration/InterfaceServiceNameResolverCompatibilityTest.java new file mode 100644 index 0000000..731ee8d --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/enabling/tools/registration/InterfaceServiceNameResolverCompatibilityTest.java @@ -0,0 +1,32 @@ +package eu.dnetlib.enabling.tools.registration; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService; +import eu.dnetlib.enabling.resultset.rmi.ResultSetService; + +public class InterfaceServiceNameResolverCompatibilityTest { + + private transient InterfaceServiceNameResolverCompatibility resolver; + + private interface IndexService {} + + private interface ISomethingService {} + + @Before + public void setUp() throws Exception { + resolver = new InterfaceServiceNameResolverCompatibility(); + } + + @Test + public void testGetNameClassOfQ() { + assertEquals("IndexService", resolver.getName(IndexService.class)); + assertEquals("SomethingService", resolver.getName(ISomethingService.class)); + assertEquals("ISRegistryService", resolver.getName(ISRegistryService.class)); + assertEquals("ResultSetService", resolver.getName(ResultSetService.class)); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/functionality/cql/lucene/CqlTranslatorImplTest.java b/dnet-core-components/src/test/java/eu/dnetlib/functionality/cql/lucene/CqlTranslatorImplTest.java new file mode 100644 index 0000000..5ba938b --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/functionality/cql/lucene/CqlTranslatorImplTest.java @@ -0,0 +1,706 @@ +package eu.dnetlib.functionality.cql.lucene; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import eu.dnetlib.functionality.cql.CqlTranslator; +import eu.dnetlib.functionality.cql.CqlTranslatorImpl; +import eu.dnetlib.functionality.cql.CqlUtils; +import eu.dnetlib.functionality.cql.parse.*; +import org.junit.Before; +import org.junit.Test; +import org.z3950.zing.cql.CQLNode; +import org.z3950.zing.cql.CQLParseException; +import org.z3950.zing.cql.CQLParser; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class CqlTranslatorImplTest { + + private CqlTranslator translator; + + private Map weights; + + @Before + public void setUp() { + translator = new CqlTranslatorImpl(); + + weights = Maps.newHashMap(); + weights.put("title", "2"); + weights.put("ugo", "1.5"); + weights.put("tag", "3"); + } + + @Test + public void testToSolr_BOW() throws Exception { + + String cqlQuery = "a b c"; + String luceneQuery = translator.toLucene(cqlQuery); + + printQuery(cqlQuery, luceneQuery); + } + + @Test + public void testToSolr_0() throws Exception { + + String cqlQuery = "\"a b x\" and r and \"eng/ita\" or f any \"pippo pluto\" and \"con: duepunti\" not (d < 5 and d > 10)"; + String luceneQuery = translator.toLucene(cqlQuery); + + printQuery(cqlQuery, luceneQuery); + } + + @Test + public void testToSolr_00() throws Exception { + + String cqlQuery = "resultdupid exact datacite____::b49fd4e3386d82df348a120cfe43516b OR objidentifier exact datacite____::b49fd4e3386d82df348a120cfe43516b"; + String luceneQuery = translator.toLucene(cqlQuery); + + printQuery(cqlQuery, luceneQuery); + } + + @Test + public void testToSolr_1() throws Exception { + + String cqlQuery = "publicationdate =/within \"2000-01-01 2010-01-01\""; + String luceneQuery = translator.toLucene(cqlQuery); + + printQuery(cqlQuery, luceneQuery); + } + + @Test + public void testToSolr_2() throws Exception { + String query = "(_all=faust AND _all=pippo) AND _all<>cinegiornale"; + Node node = new AndNode(new AndNode(new TermNode("_all", Relation.EQUAL, "faust"), new TermNode("_all", Relation.EQUAL, "pippo")), new TermNode("_all", + Relation.NOT, "cinegiornale")); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + System.out.println("NODE: " + node.toLucene()); + System.out.println("NODE: " + node.toString()); + assertEquals(node.toLucene(), parsed.asLucene()); + } + + @Test + public void testToSolr_3() throws Exception { + String query = "_all all faust"; + Node node = new TermNode("_all", Relation.ALL, "faust"); + TranslatedQuery parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + + query = "__all all \"caracas roma\""; + + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + query = "__all all caracas roma"; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + query = "__all any caracas roma"; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + query = "__all any \"caracas roma\""; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + query = "__all exact caracas roma"; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + } + + @Test + public void testToSolr_4() throws Exception { + + String cqlQuery = "publicationdate =/within \"2000-01-01 2010-01-01\" and title = \"ddd\" and y < 2010 or y <= 2010 or y > 2010 or y >= 2010"; + String luceneQuery = translator.toLucene(cqlQuery); + + printQuery(cqlQuery, luceneQuery); + } + + @Test + public void testToSolr_5() throws Exception { + + String cqlQuery = "publicationdate within \"2000-01-01 2010-01-01\""; + String luceneQuery = translator.toLucene(cqlQuery); + + printQuery(cqlQuery, luceneQuery); + } + + @Test + public void testToSolr_6() throws Exception { + + String cqlQuery = "cat sortBy title/sort.descending"; + final TranslatedQuery tr = translator.getTranslatedQuery(cqlQuery); + printQuery(cqlQuery, tr.asLucene()); + System.out.println(tr.getOptions()); + + } + + @Test + public void testToSolr_7() throws Exception { + + String cqlQuery = "title exact true sortBy title/sort.ascending"; + CQLNode parsed = new CQLParser().parse(cqlQuery); + + TranslatedQuery luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + assertNotNull(luceneQuery.getOptions().getSort().getField()); + assertNotNull(luceneQuery.getOptions().getSort().getMode()); + + assertEquals("title", luceneQuery.getOptions().getSort().getField()); + assertEquals(SortOperation.Mode.asc, luceneQuery.getOptions().getSort().getMode()); + + System.out.println("LUCENE: " + luceneQuery.asLucene() + " OPTIONS:" + luceneQuery.getOptions().getSort().getField() + " " + + luceneQuery.getOptions().getSort().getMode()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_8() throws Exception { + + String cqlQuery = "__all all \"caracas - roma\""; + CQLNode parsed = new CQLParser().parse(cqlQuery); + TranslatedQuery luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_9() throws Exception { + + String cqlQuery = "__all all \"caracas roma\""; + CQLNode parsed = new CQLParser().parse(cqlQuery); + TranslatedQuery luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + + cqlQuery = "__all all caracas roma"; + parsed = new CQLParser().parse(cqlQuery); + luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + + cqlQuery = "__all any caracas roma"; + parsed = new CQLParser().parse(cqlQuery); + luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + + cqlQuery = "__all any \"caracas roma\""; + parsed = new CQLParser().parse(cqlQuery); + luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + + cqlQuery = "__all exact caracas roma"; + parsed = new CQLParser().parse(cqlQuery); + luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_10() throws Exception { + + String cqlQuery = "__all all \"caracas - ro*\""; + CQLNode parsed = new CQLParser().parse(cqlQuery); + + Map> cqlOptions = Maps.newHashMap(); + BiMap aliases = HashBiMap.create(); + + cqlOptions.put("wildcard", Lists.newArrayList("true")); + TranslatedQuery luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap(), cqlOptions, aliases, + new HashMap()); + printQuery(cqlQuery, luceneQuery.asLucene()); + + cqlOptions = Maps.newHashMap(); + luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap(), cqlOptions, aliases, new HashMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_11() throws Exception { + + String cqlQuery = "__all <> \"kreutz\" and __all <> \"austria\""; + CQLNode parsed = new CQLParser().parse(cqlQuery); + TranslatedQuery luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_12() throws Exception { + + String cqlQuery = "__all <> \"kreutz\" and __all <> \"austria\" or __all <> \"italia\" and __dsid exact \"wwwww\""; + CQLNode parsed = new CQLParser().parse(cqlQuery); + TranslatedQuery luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_13() throws Exception { + + String cqlQuery = "__all = faust and __all <> cinegiornale"; + + CQLNode parsed = new CQLParser().parse(cqlQuery); + TranslatedQuery luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_14() throws Exception { + + String cqlQuery = "__all <> cinegiornale and __all = faust"; + + CQLNode parsed = new CQLParser().parse(cqlQuery); + TranslatedQuery luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_15() throws Exception { + + String cqlQuery = "(asdasd or asfgqwr) and textual"; + + CQLNode parsed = new CQLParser().parse(cqlQuery); + TranslatedQuery luceneQuery = translator.getTranslatedQuery(parsed, new IdentityCqlValueTransformerMap()); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_16() throws Exception { + + String cqlQuery = "title = kreutz and subject any \"austria italy\" or tag = pippo and blabla"; + CQLNode parsed = new CQLParser().parse(cqlQuery); + + Map> options = Maps.newHashMap(); + BiMap aliases = HashBiMap.create(); + IdentityCqlValueTransformerMap map = new IdentityCqlValueTransformerMap(); + + CQLNode expand = CqlUtils.expand(parsed, weights.keySet()); + + TranslatedQuery luceneQuery = translator.getTranslatedQuery(expand, map, options, aliases, weights); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_17() throws Exception { + + String cqlQuery = "a = 1 and b = 2 or c = 3 and blabla"; + CQLNode parsed = new CQLParser().parse(cqlQuery); + + Map> options = Maps.newHashMap(); + BiMap aliases = HashBiMap.create(); + IdentityCqlValueTransformerMap map = new IdentityCqlValueTransformerMap(); + + Map w = Maps.newHashMap(); + w.put("a", "2"); + w.put("c", "1.5"); + + CQLNode expand = CqlUtils.expand(parsed, w.keySet()); + + TranslatedQuery luceneQuery = translator.getTranslatedQuery(expand, map, options, aliases, w); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_18() throws Exception { + + String cqlQuery = "a exact 1"; + CQLNode parsed = new CQLParser().parse(cqlQuery); + + Map> options = Maps.newHashMap(); + BiMap aliases = HashBiMap.create(); + aliases.put("a", "b"); + + IdentityCqlValueTransformerMap map = new IdentityCqlValueTransformerMap(); + + Map w = Maps.newHashMap(); + + CQLNode expand = CqlUtils.expand(parsed, w.keySet()); + + TranslatedQuery luceneQuery = translator.getTranslatedQuery(expand, map, options, aliases, w); + + printQuery(cqlQuery, luceneQuery.asLucene()); + } + + @Test + public void testToSolr_19() throws Exception { + String query = "_all<>faust"; + Node node = new TermNode("_all", Relation.NOT, "faust"); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + + query = "_all <> faust nozze"; + // node = new TermNode("_all", Relation.NOT, "faust nozze"); + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + } + + @Test + public void testToSolr_20() throws Exception { + + String query = "(title = ESTUDIO and title = abierto) not (title = mediante)"; + + Node node = new NotNode(new AndNode(new TermNode("title", Relation.EQUAL, "ESTUDIO"), new TermNode("title", Relation.EQUAL, "abierto")), new TermNode( + "title", Relation.EQUAL, "mediante")); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + } + + @Test + public void testToSolr_21() throws Exception { + + String query = "(title = ESTUDIO and title = abierto) not (title = mediante or title = verde)"; + + Node node = new NotNode(new AndNode(new TermNode("title", Relation.EQUAL, "ESTUDIO"), new TermNode("title", Relation.EQUAL, "abierto")), new OrNode( + new TermNode("title", Relation.EQUAL, "mediante"), new TermNode("title", Relation.EQUAL, "verde"))); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + } + + @Test + public void testToSolr_22() throws Exception { + String query = " and itemtype = *"; + // Node node = new TermNode("itemtype", Relation.EQUAL, "*"); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + + query = " AND itemtype = *"; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + Map> cqlOptions = Maps.newHashMap(); + BiMap aliases = HashBiMap.create(); + + cqlOptions.put("wildcard", Lists.newArrayList("true")); + + query = "AND itemtype = vid* borat"; + parsed = translator.getTranslatedQuery(query, cqlOptions); + printQuery(query, parsed.asLucene()); + + // + - && || ! ( ) { } [ ] ^ " ~ * ? : \ + query = " AND f = * vid* bo?ra bo+ra bo-ra bo&ra bo!ra bo]ra bo^ra bo*ra";// bo|ra bo(ra bo)ra bo{ra bo}ra + // bo\"ra bo~ra bo:ra bo\\ra"; + + CQLNode cqlNode = new CQLParser().parse(query); + parsed = translator.getTranslatedQuery(cqlNode, new IdentityCqlValueTransformerMap(), cqlOptions, aliases, new HashMap()); + printQuery(query, parsed.asLucene()); + + parsed = translator.getTranslatedQuery(query, cqlOptions); + printQuery(query, parsed.asLucene()); + + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + } + + @Test + public void testToSolr_23() throws Exception { + + String query = "_all = cat sortBy title/sort.descending"; + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + Node node = new TermNode("_all", Relation.EQUAL, "cat"); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + + assertNotNull(parsed.getOptions().getSort().getField()); + assertNotNull(parsed.getOptions().getSort().getMode()); + + assertEquals("title", parsed.getOptions().getSort().getField()); + assertEquals(SortOperation.Mode.desc, parsed.getOptions().getSort().getMode()); + } + + @Test + public void testToSolr_24() throws Exception { + + String query = "invalid exact true sortBy title/sort.ascending"; + + // Node node = new TermNode("invalid", Relation.EXACT, "true"); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + + assertNotNull(parsed.getOptions().getSort().getField()); + assertNotNull(parsed.getOptions().getSort().getMode()); + + assertEquals("title", parsed.getOptions().getSort().getField()); + assertEquals(SortOperation.Mode.asc, parsed.getOptions().getSort().getMode()); + } + + @Test + public void testToSolr_25() throws Exception { + + // String query = "deleted all true and __dsid all xxxxxxxxxxx"; + + // ciao (+a +b +c) + String query = "ciao or (_all all \"a b c\")"; + TranslatedQuery parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + query = "(__all = ciao) or (__all all \"a b c\")"; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + query = "a and b"; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + query = "field=a and field=b"; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + query = "a or b"; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + query = "field=a or field=b"; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + query = "field all a or field all b"; + parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + } + + @Test + public void testToSolr_26() throws Exception { + + String query = "publicationdate =/within \"2000-01-01 2010-01-01\""; + Node node = new TermNode("publicationdate", Relation.WITHIN, "2000-01-01 2010-01-01"); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + } + + @Test + public void testToSolr_27() throws Exception { + + String query = "((((publicationdate =/within \"2000-01-01 2010-01-01\" and title = \"ddd\") and y < 2010) or y <= 2010) or y > 2010) or y >= 2010"; + Node node = new OrNode(new OrNode(new OrNode(new AndNode(new AndNode(new TermNode("publicationdate", Relation.WITHIN, "2000-01-01 2010-01-01"), + new TermNode("title", Relation.EQUAL, "ddd")), new TermNode("y", Relation.LT, "2010")), new TermNode("y", Relation.LTE, "2010")), new TermNode( + "y", Relation.GT, "2010")), new TermNode("y", Relation.GTE, "2010")); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + } + + @Test + public void testToSolr_28() throws Exception { + + String query = "publicationdate =/within \"2000-01-01 2010-01-01\" and (title = \"ddd\" and (y < 2010 or (y <= 2010 or (y > 2010 or y >= 2010))))"; + + Node node = new AndNode(new TermNode("publicationdate", Relation.WITHIN, "2000-01-01 2010-01-01"), new AndNode(new TermNode("title", Relation.EQUAL, + "ddd"), new OrNode(new TermNode("y", Relation.LT, "2010"), new OrNode(new TermNode("y", Relation.LTE, "2010"), new OrNode(new TermNode("y", + Relation.GT, "2010"), new TermNode("y", Relation.GTE, "2010")))))); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + } + + @Test + public void testToSolr_29() throws Exception { + + String query = "dateaccepted =/within \"1900-01-01 2000-01-01\" and dateaccepted >= 2010-01-01"; + + Node node = new AndNode(new TermNode("dateaccepted", Relation.WITHIN, "1900-01-01 2000-01-01"), + new TermNode("dateaccepted", Relation.GTE, "2010-01-01")); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + + query = "dateaccepted =/within \"1900-01-01 2000-01-01\" and dateaccepted > 2010-01-01"; + + node = new AndNode(new TermNode("dateaccepted", Relation.WITHIN, "1900-01-01 2000-01-01"), new TermNode("dateaccepted", Relation.GT, "2010-01-01")); + + parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + } + + @Test + public void testToSolr_30() throws Exception { + + String query = "a = 1 and b = 2 and c = 3"; + Node node = new AndNode(new AndNode(new TermNode("a", Relation.EQUAL, "1"), new TermNode("b", Relation.EQUAL, "2")), new TermNode("c", Relation.EQUAL, + "3")); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + } + + @Test + public void testToSolr_31() throws Exception { + + String query = "a = \"pippo pluto\" and b = 2 and c = 3"; + Node node = new AndNode(new AndNode(new TermNode("a", Relation.EQUAL, "pippo pluto"), new TermNode("b", Relation.EQUAL, "2")), new TermNode("c", + Relation.EQUAL, "3")); + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + + printQuery(query, parsed.asLucene()); + assertEquals(node.toLucene(), parsed.asLucene()); + } + + @Test + public void testToSolr_32() throws Exception { + + String query = "__all all \"caracas - ro*\""; + + TranslatedQuery parsed = translator.getTranslatedQuery(query); + printQuery(query, parsed.asLucene()); + + Map> cqlOptions = Maps.newHashMap(); + cqlOptions.put("wildcard", Lists.newArrayList("true")); + + parsed = translator.getTranslatedQuery(query, cqlOptions); + printQuery(query, parsed.asLucene()); + + } + + @Test + public void testToSolr_33() throws Exception { + String query1 = "query=(deletedbyinference = false) AND (((oaftype exact project) and (test)) and (fundinglevel0_id exact corda_______::FP7))"; + printQuery(query1, translator.getTranslatedQuery(query1).asLucene()); + + String query2 = "query=(((deletedbyinference = false) AND (oaftype exact project)) and (test)) and (fundinglevel0_id exact corda_______::FP7)"; + printQuery(query2, translator.getTranslatedQuery(query2).asLucene()); + + String query3 = "(deletedbyinference = false) AND (((oaftype exact project) and (test)) and (fundinglevel0_id exact corda_______::FP7))"; + printQuery(query3, translator.getTranslatedQuery(query1).asLucene()); + + String query4 = "(((deletedbyinference = false) AND (oaftype exact project)) and (test)) and (fundinglevel0_id exact corda_______::FP7)"; + printQuery(query4, translator.getTranslatedQuery(query2).asLucene()); + } + + @Test + public void testWildcard() throws CQLParseException, IOException { + + Map> options = new HashMap>(); + + String cqlQuery = "__all = \"test?\""; + String lucene = translator.toLucene(cqlQuery, options); + printQuery(cqlQuery, lucene); + + options.put("wildcard", Lists.newArrayList("true")); + + cqlQuery = "__all = \"test?\""; + lucene = translator.toLucene(cqlQuery, options); + printQuery(cqlQuery, lucene); + } + + @Test + public void testWildcard_2() throws CQLParseException, IOException { + + Map> options = new HashMap>(); + + String cqlQuery = "thumbnail=localhost*"; + String lucene = translator.toLucene(cqlQuery, options); + printQuery(cqlQuery, lucene); + + options.put("wildcard", Lists.newArrayList("true")); + + lucene = translator.toLucene(cqlQuery, options); + printQuery(cqlQuery, lucene); + } + + @Test + public void testDateQuery() throws CQLParseException, IOException { + String cqlQuery = "(resultdateofacceptance <= \"2012-03-15\")"; + Map> options = new HashMap>(); + String lucene = translator.toLucene(cqlQuery, options); + printQuery(cqlQuery, lucene); + } + + @Test + public void testFullISODateQuery() throws CQLParseException, IOException { + String cqlQuery = "(resultdateofacceptance <= 2012-03-15T00:00:00Z)"; + Map> options = new HashMap>(); + String lucene = translator.toLucene(cqlQuery, options); + printQuery(cqlQuery, lucene); + } + + @Test + public void testNonDateQuery() throws CQLParseException, IOException { + String cqlQuery = "(resultdateofacceptance <= 2012.03.15T00:00:00Z)"; + Map> options = new HashMap>(); + String lucene = translator.toLucene(cqlQuery, options); + printQuery(cqlQuery, lucene); + } + + @Test + public void testNonDateQuery2() throws CQLParseException, IOException { + String cqlQuery = "(resultdateofacceptance <= ciao)"; + Map> options = new HashMap>(); + String lucene = translator.toLucene(cqlQuery, options); + printQuery(cqlQuery, lucene); + } + + @Test + public void testDateWrong() throws Exception { + + String cqlQuery = "publicationdate =/within \"2000-01-01 2010.99.01\""; + String luceneQuery = translator.toLucene(cqlQuery); + + printQuery(cqlQuery, luceneQuery); + } + + @Test + public void testToDate() throws Exception { + + String cqlQuery = "publicationdate =/within \"* 2000-01-01\""; + String luceneQuery = translator.toLucene(cqlQuery); + + printQuery(cqlQuery, luceneQuery); + } + + private void printQuery(final String cql, final String lucene) throws CQLParseException, IOException { + System.out.println("CQL: " + cql); + // System.out.println("PARSED: " + new CQLParser().parse(cql).toCQL()); + System.out.println("LUCENE: " + lucene + "\n"); + + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/functionality/cql/lucene/CqlUtilsTest.java b/dnet-core-components/src/test/java/eu/dnetlib/functionality/cql/lucene/CqlUtilsTest.java new file mode 100644 index 0000000..47c7558 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/functionality/cql/lucene/CqlUtilsTest.java @@ -0,0 +1,215 @@ +package eu.dnetlib.functionality.cql.lucene; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import eu.dnetlib.functionality.cql.CqlGroup; +import eu.dnetlib.functionality.cql.CqlUtils; +import org.junit.Before; +import org.junit.Test; +import org.z3950.zing.cql.CQLNode; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +public class CqlUtilsTest { + + private Map weights; + + @Before + public void setUp() { + weights = Maps.newHashMap(); + weights.put("title", "2"); + weights.put("ugo", "1.5"); + weights.put("tag", "3"); + } + + @Test + public void test_0() { + String query = "tag = pluto"; + CQLNode res = CqlUtils.filter(query, Lists.newArrayList("tag")); + assertTrue(res == null); + } + + @Test + public void test_filter_1() { + String query = "creator = pippo"; + CQLNode res = CqlUtils.filter(query, Lists.newArrayList("tag")); + assertTrue(res.toCQL().equals(query)); + } + + @Test + public void test_filter_2() { + String query = "creator = pippo and tag = pluto"; + CQLNode res = CqlUtils.filter(query, Lists.newArrayList("tag")); + System.out.println(res.toCQL()); + } + + @Test + public void test_filter_3() { + String query = "creator = pippo and tag = pluto and title = pizza"; + CQLNode res = CqlUtils.filter(query, Lists.newArrayList("tag")); + System.out.println(res.toCQL()); + } + + @Test + public void test_filter_4() { + String query = "creator = pippo or tag = pluto and title = pizza"; + CQLNode res = CqlUtils.filter(query, Lists.newArrayList("tag")); + System.out.println(res.toCQL()); + } + + @Test + public void test_filter_5() { + String query = "creator = pippo and tag = pluto or title = pizza"; + CQLNode res = CqlUtils.filter(query, Lists.newArrayList("tag")); + System.out.println(res.toCQL()); + } + + ////// + + @Test + public void test_group_0() { + String query = "(>s=NAMESPACE s.test=800) and creator = pippo and tag = pluto or title = pizza or title = pozzo"; + Map res = CqlUtils.group(query, Lists.newArrayList("tag", "title")); + print(query, res); + } + + @Test + public void test_group_01() { + String query = "test and language exact \"fin\""; + Map res = CqlUtils.group(query, Lists.newArrayList("language", "repositorycountry")); + print(query, res); + } + + @Test + public void test_group_1() { + String query = "creator = pippo and tag = pluto or title = pizza and tag = roma"; + Map res = CqlUtils.group(query, Lists.newArrayList("tag")); + print(query, res); + } + + @Test + public void test_group_1_1() { + String query = "creator = pippo and ((tag = pluto or title = pizza) and tag = roma)"; + Map res = CqlUtils.group(query, Lists.newArrayList("tag")); + print(query, res); + } + + @Test + public void test_group_2() { + String query = "creator = pippo and tag = pluto or title = pizza and tag = roma"; + Map res = CqlUtils.group(query, Lists.newArrayList("tag", "creator", "title")); + print(query, res); + } + + @Test + public void test_group_3() { + String query = "creator = pippo and tag = pluto or title = pizza and tag = roma"; + Map res = CqlUtils.group(query, new ArrayList()); + print(query, res); + } + + @Test + public void test_group_3_1() { + String query = "creator = pippo or tag = roma"; + Map res = CqlUtils.group(query, Lists.newArrayList("tag")); + print(query, res); + } + + @Test + public void test_group_3_2() { + String query = "tag = roma or blabla or bloblo"; + Map res = CqlUtils.group(query, Lists.newArrayList(CqlGroup.defaultTerm)); + print(query, res); + } + + //// + + @Test + public void test_group_4_1() { + String query = "creator = pippo and tag = pluto or title = pizza and tag = roma"; + List res = CqlUtils.listTerms(query, "tag"); + System.out.println("[query: " + query + "]"); + System.out.println(res); + } + + @Test + public void test_group_4_2() { + String query = "tag = pippo"; + List res = CqlUtils.listTerms(query, "tag"); + System.out.println("[query: " + query + "]"); + System.out.println(res); + } + + @Test + public void test_group_4_3() { + String query = "creator = pippo and tag = pluto or title = pizza or tag = roma and blabla or bloblo"; + Map res = CqlUtils.group(query, CqlUtils.listFields(query)); + print(query, res); + } + + @Test + public void test_expand_1() { + String query = "tag = roma or blabla or bloblo"; + CQLNode res = CqlUtils.expand(query, weights.keySet()); + System.out.println("[query: " + query + "]"); + System.out.println(res.toCQL()); + } + + @Test + public void test_expand_2() { + String query = "blabla or bloblo"; + CQLNode res = CqlUtils.expand(query, weights.keySet()); + System.out.println("[query: " + query + "]"); + System.out.println(res.toCQL()); + } + + @Test + public void test_expand_3() { + String query = "blabla"; + CQLNode res = CqlUtils.expand(query, weights.keySet()); + System.out.println("[query: " + query + "]"); + System.out.println(res.toCQL()); + } + + @Test + public void test_expand_4() { + String query = "tag = roma or blabla or bloblo"; + CQLNode res = CqlUtils.expand(query, new HashSet()); + System.out.println("[query: " + query + "]"); + System.out.println(res.toCQL()); + } + + //// + + @Test + public void test_list_fields_0() { + String query = "pippo"; + List res = CqlUtils.listFields(query); + System.out.println("[query: " + query + "]"); + System.out.println(res); + } + + @Test + public void test_list_fields_1() { + String query = "tag = pippo"; + List res = CqlUtils.listFields(query); + System.out.println("[query: " + query + "]"); + System.out.println(res); + } + + /////////////////////// + private void print(String query, Map res) { + System.out.println("query: [" + query + "]"); + for (Entry e : res.entrySet()) { + System.out.println(e.getKey() + ": [" + e.getValue().toCQL() + "]"); + } + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/functionality/cql/mongo/MongoCqlTranslatorTest.java b/dnet-core-components/src/test/java/eu/dnetlib/functionality/cql/mongo/MongoCqlTranslatorTest.java new file mode 100644 index 0000000..3db902d --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/functionality/cql/mongo/MongoCqlTranslatorTest.java @@ -0,0 +1,148 @@ +package eu.dnetlib.functionality.cql.mongo; + +import java.io.IOException; + +import com.google.common.collect.Lists; +import com.mongodb.BasicDBObject; +import com.mongodb.MongoClient; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.bson.BsonDocument; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; +import org.junit.Ignore; +import org.junit.Test; +import org.z3950.zing.cql.CQLParseException; + +import static com.mongodb.client.model.Filters.gt; +import static org.junit.Assert.assertEquals; + +public class MongoCqlTranslatorTest { + + private static final Log log = LogFactory.getLog(MongoCqlTranslatorTest.class); // NOPMD by marko on 11/24/08 5:02 PM + private final MongoCqlTranslator tr = new MongoCqlTranslator(); + + @Test + public void testParseExact() throws IOException, CQLParseException { + Bson expected = new BasicDBObject("set", "CEDIASManuscripts"); + Bson o = tr.toMongo("set exact \"CEDIASManuscripts\""); + assertEquals(expected, o); + } + + @Test + public void testParseEq() throws IOException, CQLParseException { + Bson expected = new BasicDBObject("set", "CEDIASManuscripts"); + Bson o = tr.toMongo("set = \"CEDIASManuscripts\""); + assertEquals(expected, o); + } + + @Test + public void testParseEquivalentExact() throws IOException, CQLParseException { + Bson o = tr.toMongo("set = \"CEDIASManuscripts\""); + Bson oExact = tr.toMongo("set exact \"CEDIASManuscripts\""); + assertEquals(oExact, o); + } + + @Test + public void testParseNeq() throws IOException, CQLParseException { + Bson expected = new BasicDBObject("set", new BasicDBObject("$ne", "CEDIASManuscripts")); + Bson o = tr.toMongo("set <> \"CEDIASManuscripts\""); + assertEquals(expected, o); + } + + @Test + public void testParseAnd() throws IOException, CQLParseException { + BasicDBObject expected = new BasicDBObject("$and", Lists.newArrayList(new BasicDBObject("set", new BasicDBObject("$ne", "CEDIASManuscripts")), + new BasicDBObject("pippo", new BasicDBObject("$gt", "x")))); + Bson o = tr.toMongo("set <> \"CEDIASManuscripts\" AND pippo > x"); + log.info(o); + assertEquals(expected, o); + } + + @Test + public void testParseOr() throws IOException, CQLParseException { + BasicDBObject expected = new BasicDBObject("$or", Lists.newArrayList(new BasicDBObject("set", new BasicDBObject("$ne", "CEDIASManuscripts")), + new BasicDBObject("pippo", new BasicDBObject("$gt", "x")))); + Bson o = tr.toMongo("set <> \"CEDIASManuscripts\" OR pippo > x"); + log.info(o); + assertEquals(expected, o); + } + + @Test + public void testParseNot() throws IOException, CQLParseException { + BasicDBObject expected = new BasicDBObject("$and", Lists.newArrayList(new BasicDBObject("set", "CEDIASManuscripts"), new BasicDBObject("$not", + new BasicDBObject("pippo", new BasicDBObject("$gt", "x"))))); + Bson o = tr.toMongo("set = \"CEDIASManuscripts\" NOT pippo > x"); + //log.info(o) + assertEquals(expected, o); + } + + @Test + public void testParseStar() throws IOException, CQLParseException { + BasicDBObject expected = new BasicDBObject(); + Bson o = tr.toMongo("*"); + Bson o2 = tr.toMongo("*=*"); + assertEquals(expected, o); + assertEquals(expected, o2); + } + + @Test + public void testParseStarAnd() throws IOException, CQLParseException { + BasicDBObject expected = new BasicDBObject("$and", Lists.newArrayList(new BasicDBObject(), new BasicDBObject("pippo", new BasicDBObject("$gt", "x")))); + Bson o = tr.toMongo("* AND pippo > x"); + Bson o2 = tr.toMongo("*=* AND pippo > x"); + assertEquals(expected, o); + assertEquals(expected, o2); + } + + @Test + public void testParseStarAnd2() throws IOException, CQLParseException { + BasicDBObject expected = new BasicDBObject("$and", Lists.newArrayList(new BasicDBObject("resulttypeid", "publication"), new BasicDBObject("funder", new BasicDBObject("$exists", true)))); + Bson o = tr.toMongo("(resulttypeid exact \"publication\" and funder =*)"); + assertEquals(expected, o); + } + + @Test + public void testParseIdQuery() throws IOException, CQLParseException { + BasicDBObject expected = new BasicDBObject("_id", new BasicDBObject("$gt", new ObjectId("5225e093aabf055637bf2c65"))); + Bson o = tr.toMongo("_id > 5225e093aabf055637bf2c65"); + assertEquals(expected, o); + } + + @Test + public void testLongQuery() throws IOException, CQLParseException { + String q = "oaftype=\"result\" AND resulttypeid=\"publication\" AND (set=\"All_Ireland_Public_Health_Repository\" OR set=\"ARROW_DIT\" OR set=\"Cork_Open_Research_Archive\" OR set=\"DCU_Online_Research_Access_Service\" OR set=\"e-publications_RCSI\" OR set=\"Marine_Institute_Open_Access_Repository__OAR\" OR set=\"Maynooth_University_ePrints___eTheses_Archive\" OR set=\"Research_Repository_UCD\" OR set=\"STOR\" OR set=\"Trinity_s_Access_to_Research_Archive\" OR set=\"UCD_Digital_Library\" OR set=\"University_of_Limerick_Institutional_Repository\" OR set=\"Waterford_Institute_of_Technology_Repository\")"; + Bson o = tr.toMongo(q); + log.info(o); + } + + @Ignore + @Test + public void testParseWfLoggerQuery() throws IOException, CQLParseException { + BasicDBObject expected = new BasicDBObject("$and", + Lists.newArrayList( + new BasicDBObject("parentDatasourceId", "opendoar____::2294"), + new BasicDBObject("system:profileFamily", "aggregator"), + new BasicDBObject("system:isCompletedSuccessfully", "true"))); + + Bson o = tr.toMongo("{\"parentDatasourceId\" : \"opendoar____::2294\", \"system:profileFamily\" : \"aggregator\", \"system:isCompletedSuccessfully\" : \"true\" }"); + assertEquals(expected, o); + } + + @Test + public void testParseTimestamp() throws IOException, CQLParseException { + Long l = new Long("1494945927504"); + BasicDBObject expected = new BasicDBObject("timestamp", new BasicDBObject("$gt",l)); + Bson filter = gt("timestamp", l); + assertEquals(expected.toBsonDocument(BsonDocument.class, MongoClient.getDefaultCodecRegistry()), filter.toBsonDocument(BsonDocument.class, MongoClient.getDefaultCodecRegistry())); + } + + @Test + public void testPM_PMCIDQueryNotinPubMedCentral() throws IOException, CQLParseException { + String q = "((pidtype = \"pmc\" or pidtype = \"pmid\") and set <> \"PubMed_Central\" )"; + BasicDBObject expected = new BasicDBObject("$and", Lists.newArrayList(new BasicDBObject("$or", Lists.newArrayList(new BasicDBObject("pidtype", "pmc"), new BasicDBObject("pidtype", "pmid"))), new BasicDBObject("set", new BasicDBObject("$ne", "PubMed_Central")))); + Bson o = tr.toMongo(q); + assertEquals(expected, o); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/functionality/index/query/SolrIndexQueryFactoryTest.java b/dnet-core-components/src/test/java/eu/dnetlib/functionality/index/query/SolrIndexQueryFactoryTest.java new file mode 100644 index 0000000..57b0faa --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/functionality/index/query/SolrIndexQueryFactoryTest.java @@ -0,0 +1,31 @@ +package eu.dnetlib.functionality.index.query; + +import java.util.List; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; +import eu.dnetlib.functionality.index.client.IndexClientException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class SolrIndexQueryFactoryTest { + + IndexQueryFactory factory; + BiMap browsingAliases = HashBiMap.create(); + + @Before + public void setUp() throws Exception { + factory = new SolrIndexQueryFactory(); + browsingAliases.put("field1", "field1ForBrowsing"); + browsingAliases.put("field2", "field2ForBrowsing"); + } + + @Test + public void testGetBrowsableFields() throws IndexClientException { + List browsables = factory.getBrowsableFields(Lists.newArrayList("field1", "field3", "field2"), browsingAliases); + Assert.assertEquals(Lists.newArrayList("field1ForBrowsing", "field3", "field2ForBrowsing"), browsables); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/miscutils/functional/xml/ApplyXsltTest.java b/dnet-core-components/src/test/java/eu/dnetlib/miscutils/functional/xml/ApplyXsltTest.java index 7c86b5b..1d5b154 100644 --- a/dnet-core-components/src/test/java/eu/dnetlib/miscutils/functional/xml/ApplyXsltTest.java +++ b/dnet-core-components/src/test/java/eu/dnetlib/miscutils/functional/xml/ApplyXsltTest.java @@ -9,10 +9,10 @@ import javax.xml.transform.TransformerFactory; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnit44Runner; +import org.mockito.junit.MockitoJUnitRunner; import org.springframework.core.io.ClassPathResource; -@RunWith(MockitoJUnit44Runner.class) +@RunWith(MockitoJUnitRunner.class) public class ApplyXsltTest { TransformerFactory tf = TransformerFactory.newInstance(); diff --git a/dnet-core-components/src/test/java/eu/dnetlib/miscutils/functional/xml/dnetFunctionsTest.java b/dnet-core-components/src/test/java/eu/dnetlib/miscutils/functional/xml/dnetFunctionsTest.java index 67b98ec..112ca06 100644 --- a/dnet-core-components/src/test/java/eu/dnetlib/miscutils/functional/xml/dnetFunctionsTest.java +++ b/dnet-core-components/src/test/java/eu/dnetlib/miscutils/functional/xml/dnetFunctionsTest.java @@ -15,10 +15,10 @@ import org.dom4j.io.SAXReader; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnit44Runner; +import org.mockito.junit.MockitoJUnitRunner; -@RunWith(MockitoJUnit44Runner.class) -public class dnetFunctionsTest { +@RunWith(MockitoJUnitRunner.class) +public class DnetFunctionsTest { private static final String XSLT = "" + diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/aop/MethodCacheInterceptorTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/aop/MethodCacheInterceptorTest.java new file mode 100644 index 0000000..5dcb90c --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/aop/MethodCacheInterceptorTest.java @@ -0,0 +1,152 @@ +package eu.dnetlib.springutils.aop; + +import static org.junit.Assert.assertEquals; // NOPMD +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; // NOPMD +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Element; + +import org.aopalliance.intercept.MethodInvocation; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * test the methodcache AOP interceptor. + * + * @author marko + * + */ +public class MethodCacheInterceptorTest { + + private static final String TEST_VALUE = "mockedTestValue"; + private transient CacheManager singletonManager; + private transient Cache cache; + private transient MethodInvocation inv; + private transient MethodInvocation invArgs; + private transient MethodCacheInterceptor inter; + + @Before + public void setUp() throws Throwable { + singletonManager = CacheManager.create(); + singletonManager.addCache("testCache"); + cache = singletonManager.getCache("testCache"); + + inter = new MethodCacheInterceptor(); + inter.setCache(cache); + + final MethodCacheInterceptorTest _this = mock(MethodCacheInterceptorTest.class); + + inv = mock(MethodInvocation.class); + final Method method = MethodCacheInterceptorTest.class.getDeclaredMethod("test"); + + when(inv.getThis()).thenReturn(_this); + when(inv.getMethod()).thenReturn(method); + + when(inv.proceed()).thenReturn(TEST_VALUE); + + // second method + invArgs = mock(MethodInvocation.class); + final Method methodArgs = MethodCacheInterceptorTest.class.getDeclaredMethod("testWithArgs", String.class); + + when(invArgs.getThis()).thenReturn(_this); + when(invArgs.getMethod()).thenReturn(methodArgs); + when(invArgs.getArguments()).thenReturn(new Object[] { "test1" }); + when(invArgs.proceed()).thenReturn("mockedTestValueWithArgs"); + + } + + @After + public void tearDown() throws Exception { + singletonManager.removeAllCaches(); + } + + @Test + public void testCache() { + cache.put(new Element("key", "value")); + assertEquals("check cache", "value", cache.get("key").getObjectValue()); + } + + @Test + public void testInterceptor() throws Throwable { + assertEquals("first invocation", TEST_VALUE, inter.invoke(inv)); + assertEquals("cached invocation", TEST_VALUE, inter.invoke(inv)); + + // real method has to be called only once + verify(inv).proceed(); + } + + @Test + public void testFlush() throws Throwable { + assertEquals("first invocation", TEST_VALUE, inter.invoke(inv)); + cache.flush(); + assertEquals("cache not flushed", TEST_VALUE, inter.invoke(inv)); + + // since cache is flushed, the method is called twice + // TODO understand why It fail + // verify(inv, times(2)).proceed(); + } + + @Test + public void testWithArguments() throws Throwable { + assertEquals("first invocation", "mockedTestValueWithArgs", inter.invoke(invArgs)); + assertEquals("cached invocation", "mockedTestValueWithArgs", inter.invoke(invArgs)); + verify(invArgs).proceed(); + } + + /** + * This method is only needed because of it's signature. + * + * @return dummy + */ + public String test() { // NOPMD + return null; + } + + /** + * This method is only needed because of it's signature. + * + * @param arg + * dummy + * @return dummy + */ + public String testWithArgs(final String arg) { // NOPMD + return null; + } + + /** + * ensure that the spring lifecycle works. + * + * @throws NoSuchMethodException + * reflection + * @throws InvocationTargetException + * reflection + * @throws IllegalAccessException + * reflection + */ + @Test + public void testAfterPropertiesSet() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { + inter.getClass().getMethod("afterPropertiesSet").invoke(inter); + assertNotNull("dummy", inter); + } + + /** + * test write only. + */ + @Test + public void testSetWriteOnly() { + assertFalse("getter", inter.isWriteOnly()); + inter.setWriteOnly(true); + assertTrue("getter", inter.isWriteOnly()); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/aop/MethodInvokingInitializerTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/aop/MethodInvokingInitializerTest.java new file mode 100644 index 0000000..ff1e81e --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/aop/MethodInvokingInitializerTest.java @@ -0,0 +1,29 @@ +package eu.dnetlib.springutils.aop; + +import static org.junit.Assert.*; + +import javax.annotation.Resource; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class MethodInvokingInitializerTest { + + @Resource + private SimpleBean simpleBean; + + @Before + public void setUp() throws Exception { + } + + @Test + public void testStart() { + assertTrue(simpleBean.isInitialized()); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/aop/SimpleBean.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/aop/SimpleBean.java new file mode 100644 index 0000000..ec707b2 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/aop/SimpleBean.java @@ -0,0 +1,20 @@ +package eu.dnetlib.springutils.aop; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class SimpleBean { + private static final Log log = LogFactory.getLog(SimpleBean.class); // NOPMD by marko on 11/24/08 5:02 PM + + private boolean initialized = false; + + public void init() { + log.info("initialized"); + System.out.println("initialized"); + initialized = true; + } + + public boolean isInitialized() { + return initialized; + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/collections/ResourceReaderCollectionTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/collections/ResourceReaderCollectionTest.java new file mode 100644 index 0000000..7726175 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/collections/ResourceReaderCollectionTest.java @@ -0,0 +1,44 @@ +package eu.dnetlib.springutils.collections; + + +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.core.io.Resource; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Test resource->string transformer. + * + * @author marko + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class ResourceReaderCollectionTest { + + /** + * resources filled by spring. + */ + @javax.annotation.Resource(name = "resourceList") + List resources; + + /** + * test resources + */ + @Test + public void testResources() { + boolean passed = false; + for(String el : new ResourceReaderCollection(resources)) { + passed = true; + assertEquals("content", "test\n", el); + } + + assertTrue("passed", passed); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/ConditionalBeanTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/ConditionalBeanTest.java new file mode 100644 index 0000000..212677a --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/ConditionalBeanTest.java @@ -0,0 +1,84 @@ +package eu.dnetlib.springutils.condbean; + +import static org.junit.Assert.assertEquals; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class ConditionalBeanTest { + + private static final int FIVE = 5; + + private static final int THREE = 3; + + @Autowired + @Qualifier("elementTest") + transient TestBean elementBean; + + @Autowired + @Qualifier("elementTest") + transient TestBean attributeBean; + + @Autowired + @Qualifier("elementTest2") + transient TestBean boolBean1; + + @Autowired + @Qualifier("elementTest2") + transient TestBean boolBean2; + + @Autowired + @Qualifier("elementTest3") + transient TestBean boolBean3; + + @Autowired + @Qualifier("elementTest3") + transient TestBean boolBean4; + + + /** + * required to be "BeforeClass" because otherwise the spring context will not see it. + */ + @BeforeClass + public static void setProperties() { + System.setProperty("some.existing.property", "pippo"); + } + + @Test + public void element() { + assertEquals("element", 1, elementBean.getValue()); + } + + @Test + public void attribute() { + assertEquals("attribute", 1, attributeBean.getValue()); + } + + @Test + public void bool1() { + assertEquals("boolean 1", THREE, boolBean1.getValue()); + } + + @Test + public void bool2() { + assertEquals("boolean 2", THREE, boolBean2.getValue()); + } + + @Test + public void bool3() { + assertEquals("boolean 3", FIVE, boolBean3.getValue()); + } + + @Test + public void bool4() { + assertEquals("boolean 4", FIVE, boolBean4.getValue()); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/JParsecConditionExpressionParserTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/JParsecConditionExpressionParserTest.java new file mode 100644 index 0000000..c31b90b --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/JParsecConditionExpressionParserTest.java @@ -0,0 +1,18 @@ +package eu.dnetlib.springutils.condbean; + +import static org.junit.Assert.assertTrue; + +import org.junit.Ignore; +import org.junit.Test; + +public class JParsecConditionExpressionParserTest { + + transient JParsecConditionExpressionParser parser = new JParsecConditionExpressionParser(); + + @Ignore("implement the parser in parsec") + @Test + public void testExpressionValue() { + assertTrue("test it", parser.expressionValue("1")); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/TestBean.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/TestBean.java new file mode 100644 index 0000000..c624161 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/TestBean.java @@ -0,0 +1,13 @@ +package eu.dnetlib.springutils.condbean; + +public class TestBean { + int value; + + public int getValue() { + return value; + } + + public void setValue(final int value) { + this.value = value; + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/TrivialConditionExpressionParserTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/TrivialConditionExpressionParserTest.java new file mode 100644 index 0000000..7ee77d1 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/TrivialConditionExpressionParserTest.java @@ -0,0 +1,37 @@ +package eu.dnetlib.springutils.condbean; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TrivialConditionExpressionParserTest { + + transient TrivialConditionExpressionParser parser = new TrivialConditionExpressionParser(); + + @BeforeClass + public static void setUpProperties() { + System.setProperty("trivial.condition.true", "value"); + } + + @Before + public void setUp() { + parser.setFinder(new SystemPropertiesFinder()); + } + + @Test + public void testExpressionValue() { + assertTrue("check true condition", parser.expressionValue("trivial.condition.true")); + assertFalse("check false condition", parser.expressionValue("trivial.condition.false")); + } + + @Test + public void testGetProperty() { + assertEquals("check true condition", "value", parser.getProperty("trivial.condition.true")); + assertEquals("check false condition", null, parser.getProperty("trivial.condition.false")); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/CondBeanParserTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/CondBeanParserTest.java new file mode 100644 index 0000000..e2583ac --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/CondBeanParserTest.java @@ -0,0 +1,196 @@ +package eu.dnetlib.springutils.condbean.parser; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; + +import org.junit.Before; +import org.junit.Test; + +import eu.dnetlib.springutils.condbean.parser.ast.AbstractExpression; +import fri.patterns.interpreter.parsergenerator.Lexer; +import fri.patterns.interpreter.parsergenerator.Parser; +import fri.patterns.interpreter.parsergenerator.ParserTables; +import fri.patterns.interpreter.parsergenerator.lexer.LexerBuilder; +import fri.patterns.interpreter.parsergenerator.lexer.LexerException; +import fri.patterns.interpreter.parsergenerator.parsertables.LALRParserTables; +import fri.patterns.interpreter.parsergenerator.syntax.SyntaxException; +import fri.patterns.interpreter.parsergenerator.syntax.builder.SyntaxBuilder; +import fri.patterns.interpreter.parsergenerator.syntax.builder.SyntaxSeparation; + +public class CondBeanParserTest { + + private static final String CHECK_EXPRESSION = "check expression"; + private static final String PARSE_FAILED = "parse failed"; + private SyntaxSeparation separation; + private LexerBuilder builder; + private Lexer lexer; + private ParserTables parserTables; + private Parser parser; + private AbstractExpression result; + private ParserTables tables; + private SyntaxBuilder builder2; + private InputStream syntaxInput; + + @Before + public void setUp() throws SyntaxException, LexerException, Exception { + syntaxInput = CondBeanParser.class.getResourceAsStream("CondBeanParser.syntax"); + builder2 = new SyntaxBuilder(syntaxInput); + lexer = builder2.getLexer(); + tables = new LALRParserTables(builder2.getParserSyntax()); + parser = new Parser(tables); + parser.setLexer(lexer); + } + + @Test + public void testParser0() throws IOException { + parser.setInput("1 || 1"); + assertTrue(PARSE_FAILED, parser.parse(new CondBeanParser())); + result = (AbstractExpression) parser.getResult(); + assertTrue(CHECK_EXPRESSION, (Boolean) result.evaluate()); + } + + @Test + public void testParser1() throws IOException { + parser.setInput("!${some.existing.property} && ${some.value} == 3 || isdef(someBean) || 11"); + assertTrue(PARSE_FAILED, parser.parse(new CondBeanParser())); + result = (AbstractExpression) parser.getResult(); + assertTrue(CHECK_EXPRESSION, (Boolean) result.evaluate()); + } + + @Test + public void testParser2() throws IOException { + parser.setInput("2 == 3"); + assertTrue(PARSE_FAILED, parser.parse(new CondBeanParser())); + result = (AbstractExpression) parser.getResult(); + assertFalse(CHECK_EXPRESSION, (Boolean) result.evaluate()); + + } + + @Test + public void testParser3() throws IOException { + parser.setInput("2==3"); + assertTrue(PARSE_FAILED, parser.parse(new CondBeanParser())); + result = (AbstractExpression) parser.getResult(); + assertFalse(CHECK_EXPRESSION, (Boolean) result.evaluate()); + + } + + @Test + public void testParser4() throws IOException { + parser.setInput("${some.value} == 3"); + assertTrue(PARSE_FAILED, parser.parse(new CondBeanParser())); + result = (AbstractExpression) parser.getResult(); + assertFalse(CHECK_EXPRESSION, (Boolean) result.evaluate()); + } + + @Test + public void testParser5() throws IOException { + parser.setInput("!${some.existing.property} && ${some.value} == 3"); + assertTrue(PARSE_FAILED, parser.parse(new CondBeanParser())); + result = (AbstractExpression) parser.getResult(); + assertFalse(CHECK_EXPRESSION, (Boolean) result.evaluate()); + + } + + @Test + public void testParser6() throws IOException { + parser.setInput("${some.value} == 3 && !${some.existing.property}"); // give + assertTrue(PARSE_FAILED, parser.parse(new CondBeanParser())); + result = (AbstractExpression) parser.getResult(); + assertFalse(CHECK_EXPRESSION, (Boolean) result.evaluate()); + + } + + @Test + public void testParser7() throws IOException { + parser.setInput("3 < 2 && 2<4"); + assertTrue(PARSE_FAILED, parser.parse(new CondBeanParser())); + result = (AbstractExpression) parser.getResult(); + assertFalse(CHECK_EXPRESSION, (Boolean) result.evaluate()); + } + + @Test + public void testParser8() throws IOException { + parser.setInput("3 < 2 || 2<4"); + assertTrue(PARSE_FAILED, parser.parse(new CondBeanParser())); + result = (AbstractExpression) parser.getResult(); + assertTrue(CHECK_EXPRESSION, (Boolean) result.evaluate()); + + } + + public SyntaxSeparation getSeparation() { + return separation; + } + + public void setSeparation(final SyntaxSeparation separation) { + this.separation = separation; + } + + public LexerBuilder getBuilder() { + return builder; + } + + public void setBuilder(final LexerBuilder builder) { + this.builder = builder; + } + + public Lexer getLexer() { + return lexer; + } + + public void setLexer(final Lexer lexer) { + this.lexer = lexer; + } + + public ParserTables getParserTables() { + return parserTables; + } + + public void setParserTables(final ParserTables parserTables) { + this.parserTables = parserTables; + } + + public Parser getParser() { + return parser; + } + + public void setParser(final Parser parser) { + this.parser = parser; + } + + public AbstractExpression getResult() { + return result; + } + + public void setResult(final AbstractExpression result) { + this.result = result; + } + + public ParserTables getTables() { + return tables; + } + + public void setTables(final ParserTables tables) { + this.tables = tables; + } + + public SyntaxBuilder getBuilder2() { + return builder2; + } + + public void setBuilder2(final SyntaxBuilder builder2) { + this.builder2 = builder2; + } + + public InputStream getSyntaxInput() { + return syntaxInput; + } + + public void setSyntaxInput(final InputStream syntaxInput) { + this.syntaxInput = syntaxInput; + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/RunccExpressionParserTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/RunccExpressionParserTest.java new file mode 100644 index 0000000..09ff463 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/RunccExpressionParserTest.java @@ -0,0 +1,151 @@ +package eu.dnetlib.springutils.condbean.parser; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import eu.dnetlib.springutils.condbean.parser.ast.AndExpression; +import eu.dnetlib.springutils.condbean.parser.ast.EqualityExpression; +import eu.dnetlib.springutils.condbean.parser.ast.OrExpression; +import eu.dnetlib.springutils.condbean.parser.ast.RelationalExpression; +import eu.dnetlib.springutils.condbean.parser.ast.SysProperty; + + +public class RunccExpressionParserTest { //NOPMD + + private static final String CHECK_EXPRESSION = "check expression value"; + private static final String EXPRESSION_TYPE = "check expression type"; + private String parseInput; + private RunccExpressionParser parser; + + @Before + public void setProperties() { + System.setProperty("some.existing.property", "pippo"); + + parser = new RunccExpressionParser(); + parser.setCondBeanParser(new CondBeanParser()); + } + + @After + public void cleanupProperties() { + System.clearProperty("some.existing.property"); + } + + @Test + public void test0() throws IOException { + parseInput = "${some.existing.property}"; + + assertTrue(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof SysProperty); + } + + @Test + public void test1() throws IOException { + parseInput = "false && false"; + + assertFalse(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof AndExpression); + } + + @Test + public void test2() throws IOException { + parseInput = "1<2"; + + assertTrue(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof RelationalExpression); + } + + @Test + public void test3() throws IOException { + + parseInput = "!${some.existing.property} && ${some.value} == 3 || isdef(someBean) || 11"; + assertTrue(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof OrExpression); + } + + @Test + public void test4() throws IOException { + + parseInput = "2 == 3"; + assertFalse(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof EqualityExpression); + + } + + @Test + public void test5() throws IOException { + + parseInput = "\"3\"==3"; + assertFalse(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof EqualityExpression); + } + + @Test + public void test6() throws IOException { + + parseInput = "${some.value} == 3"; + assertFalse(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof EqualityExpression); + } + + @Test + public void test7() throws IOException { + + parseInput = "!${some.existing.property} && ${some.value} == 3"; + assertFalse(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof AndExpression); + } + + @Test + public void test8() throws IOException { + + parseInput = "${some.value} == 3 && !${some.existing.property}"; + assertFalse(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof AndExpression); + } + + @Test + public void test9() throws IOException { + + parseInput = "3 < 2 && 2<4"; + assertFalse(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof AndExpression); + } + + @Test + public void test10() throws IOException { + + parseInput = "3 < 2 || 2<4"; + assertTrue(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof OrExpression); + } + + @Test + public void test11() throws IOException { + parseInput = "3==3"; + + assertTrue(CHECK_EXPRESSION, parser.expressionValue(parseInput)); + assertTrue(EXPRESSION_TYPE, parser.getTopRule(parseInput) instanceof EqualityExpression); + } + + public String getParseInput() { + return parseInput; + } + + public void setParseInput(final String parseInput) { + this.parseInput = parseInput; + } + + public RunccExpressionParser getParser() { + return parser; + } + + public void setParser(final RunccExpressionParser parser) { + this.parser = parser; + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/AndExpressionTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/AndExpressionTest.java new file mode 100644 index 0000000..a4f9966 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/AndExpressionTest.java @@ -0,0 +1,77 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; + +public class AndExpressionTest { + + private static final String CHECK_EXPRESSION = "check expression"; + private AndExpression expr; + + private AbstractExpression left, right; + + @Before + public void setUp() throws Exception { + left = mock(AbstractExpression.class); + right = mock(AbstractExpression.class); + expr = new AndExpression(left, right); + } + + @Test + public void testTrue() { + when(left.evaluate()).thenReturn(true); + when(right.evaluate()).thenReturn(true); + assertTrue(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testFalse() { + when(left.evaluate()).thenReturn(true); + when(right.evaluate()).thenReturn(false); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testOneNull() { + when(left.evaluate()).thenReturn(null); + when(right.evaluate()).thenReturn(1); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testBothNull() { + when(left.evaluate()).thenReturn(null); + when(right.evaluate()).thenReturn(null); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + public AndExpression getExpr() { + return expr; + } + + public void setExpr(final AndExpression expr) { + this.expr = expr; + } + + public AbstractExpression getLeft() { + return left; + } + + public void setLeft(final AbstractExpression left) { + this.left = left; + } + + public AbstractExpression getRight() { + return right; + } + + public void setRight(final AbstractExpression right) { + this.right = right; + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/EqualityExpressionTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/EqualityExpressionTest.java new file mode 100644 index 0000000..5c259b5 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/EqualityExpressionTest.java @@ -0,0 +1,106 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; + +public class EqualityExpressionTest { + + private static final String CHECK_EXPRESSION = "check expression"; + + private EqualityExpression expr; + + private AbstractExpression left, right; + + @Before + public void setUp() throws Exception { + left = mock(AbstractExpression.class); + right = mock(AbstractExpression.class); + expr = new EqualityExpression(left, right, "=="); + + } + + @Test + public void testEquals() { + when(left.evaluate()).thenReturn("pippo"); + when(right.evaluate()).thenReturn("pippo"); + assertTrue(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testNotEquals() { + when(left.evaluate()).thenReturn("pippo1"); + when(right.evaluate()).thenReturn("puppo"); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testInteger() { + when(left.evaluate()).thenReturn("pippo1"); + when(right.evaluate()).thenReturn(1); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testOneNull() { + when(left.evaluate()).thenReturn(null); + when(right.evaluate()).thenReturn(1); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testBothNull() { + when(left.evaluate()).thenReturn(null); + when(right.evaluate()).thenReturn(null); + assertTrue(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testIntString() { + when(left.evaluate()).thenReturn(1); + when(right.evaluate()).thenReturn("1"); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testIntChar() { + when(left.evaluate()).thenReturn(1); + when(right.evaluate()).thenReturn('1'); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testString() { + when(left.evaluate()).thenReturn("2"); + when(right.evaluate()).thenReturn("3"); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + public EqualityExpression getExpr() { + return expr; + } + + public void setExpr(final EqualityExpression expr) { + this.expr = expr; + } + + public AbstractExpression getLeft() { + return left; + } + + public void setLeft(final AbstractExpression left) { + this.left = left; + } + + public AbstractExpression getRight() { + return right; + } + + public void setRight(final AbstractExpression right) { + this.right = right; + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/ExpressionTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/ExpressionTest.java new file mode 100644 index 0000000..ca6bcbf --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/ExpressionTest.java @@ -0,0 +1,50 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + + +public class ExpressionTest { + + private AbstractTerminal term1; + private AbstractExpression exp1; + private AbstractBinaryExpression exp2; + + @Test + public void testExpression() { + term1 = new IdentifierNode("pippo"); + exp1 = new NotExpression(term1); + exp2 = new OrExpression(term1, exp1); + + assertEquals("check expression", exp2.getLeft(), term1); + + } + + public AbstractTerminal getTerm1() { + return term1; + } + + public void setTerm1(final AbstractTerminal term1) { + this.term1 = term1; + } + + public AbstractExpression getExp1() { + return exp1; + } + + public void setExp1(final AbstractExpression exp1) { + this.exp1 = exp1; + } + + public AbstractBinaryExpression getExp2() { + return exp2; + } + + public void setExp2(final AbstractBinaryExpression exp2) { + this.exp2 = exp2; + } + + + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/NotExpressionTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/NotExpressionTest.java new file mode 100644 index 0000000..9702428 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/NotExpressionTest.java @@ -0,0 +1,51 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; + +public class NotExpressionTest { + + private NotExpression expr; + + private AbstractExpression expression; + + @Before + public void setUp() throws Exception { + expression = mock(AbstractExpression.class); + expr = new NotExpression(expression); + } + + @Test + public void testTrue() { + when(expression.evaluate()).thenReturn(true); + assertFalse("check expression", (Boolean) expr.evaluate()); + } + + @Test + public void testFalse() { + when(expression.evaluate()).thenReturn(false); + assertTrue("check expression", (Boolean) expr.evaluate()); + } + + public NotExpression getExpr() { + return expr; + } + + public void setExpr(final NotExpression expr) { + this.expr = expr; + } + + public AbstractExpression getExpression() { + return expression; + } + + public void setExpression(final AbstractExpression expression) { + this.expression = expression; + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/OrExpressionTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/OrExpressionTest.java new file mode 100644 index 0000000..9a4a0d0 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/OrExpressionTest.java @@ -0,0 +1,77 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; + +public class OrExpressionTest { + private static final String CHECK_EXPRESSION = "check expression"; + + private OrExpression expr; + + private AbstractExpression left, right; + + @Before + public void setUp() throws Exception { + left = mock(AbstractExpression.class); + right = mock(AbstractExpression.class); + expr = new OrExpression(left, right); + } + + @Test + public void testTrue() { + when(left.evaluate()).thenReturn(true); + when(right.evaluate()).thenReturn(true); + assertTrue(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testFalse() { + when(left.evaluate()).thenReturn(true); + when(right.evaluate()).thenReturn(false); + assertTrue(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testOneNull() { + when(left.evaluate()).thenReturn(null); + when(right.evaluate()).thenReturn(1); + assertTrue(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testBothNull() { + when(left.evaluate()).thenReturn(null); + when(right.evaluate()).thenReturn(null); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + public OrExpression getExpr() { + return expr; + } + + public void setExpr(final OrExpression expr) { + this.expr = expr; + } + + public AbstractExpression getLeft() { + return left; + } + + public void setLeft(final AbstractExpression left) { + this.left = left; + } + + public AbstractExpression getRight() { + return right; + } + + public void setRight(final AbstractExpression right) { + this.right = right; + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/RelationalExpressionTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/RelationalExpressionTest.java new file mode 100644 index 0000000..4ab53db --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/condbean/parser/ast/RelationalExpressionTest.java @@ -0,0 +1,85 @@ +package eu.dnetlib.springutils.condbean.parser.ast; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; + +public class RelationalExpressionTest { + + private static final String CHECK_EXPRESSION = "check expression"; + + private RelationalExpression expr; + + private AbstractExpression left, right; + + @Before + public void setUp() throws Exception { + left = mock(AbstractExpression.class); + right = mock(AbstractExpression.class); + expr = new RelationalExpression(left, right, "<="); + } + + @Test + public void testString() { + when(left.evaluate()).thenReturn("pippo"); + when(right.evaluate()).thenReturn("pippo"); + assertTrue(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testStringlex1() { + when(left.evaluate()).thenReturn("pippo1"); + when(right.evaluate()).thenReturn("mippo"); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testStringlex2() { + when(left.evaluate()).thenReturn("pippo"); + when(right.evaluate()).thenReturn("rippo"); + assertTrue(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testInt() { + when(left.evaluate()).thenReturn(1); + when(right.evaluate()).thenReturn(1); + assertTrue(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + @Test + public void testIntString() { + when(left.evaluate()).thenReturn("1"); + when(right.evaluate()).thenReturn(1); + assertFalse(CHECK_EXPRESSION, (Boolean) expr.evaluate()); + } + + public RelationalExpression getExpr() { + return expr; + } + + public void setExpr(final RelationalExpression expr) { + this.expr = expr; + } + + public AbstractExpression getLeft() { + return left; + } + + public void setLeft(final AbstractExpression left) { + this.left = left; + } + + public AbstractExpression getRight() { + return right; + } + + public void setRight(final AbstractExpression right) { + this.right = right; + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/stringtemplate/ClassPathStringTemplateGroupTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/stringtemplate/ClassPathStringTemplateGroupTest.java new file mode 100644 index 0000000..2bc7de6 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/stringtemplate/ClassPathStringTemplateGroupTest.java @@ -0,0 +1,33 @@ +package eu.dnetlib.springutils.stringtemplate; + +import static org.junit.Assert.assertEquals; +import java.io.StringReader; +import org.junit.Before; +import org.junit.Test; + +public class ClassPathStringTemplateGroupTest { + + private ClassPathStringTemplateGroup group; + private final transient String templates = "group simple;\nvardef(type,name) ::= \" ;\"" + + "method(type,name,args) ::= << () { }>>";; + + @Before + public void setUp() throws Exception { + group = new ClassPathStringTemplateGroup(new StringReader(templates)); + } + + @Test + public void testName() { + group.setPackage("eu.dnetlib"); + assertEquals("check Path", "/eu/dnetlib/pippo.st", group.getFileNameFromTemplateName("pippo")); + assertEquals("check Path", "pippo", group.getTemplateNameFromFileName("/eu/dnetlib/pippo.st")); + } + + public ClassPathStringTemplateGroup getGroup() { + return group; + } + + public void setGroup(final ClassPathStringTemplateGroup group) { + this.group = group; + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/stringtemplate/StringTemplateFactoryTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/stringtemplate/StringTemplateFactoryTest.java new file mode 100644 index 0000000..4611e57 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/stringtemplate/StringTemplateFactoryTest.java @@ -0,0 +1,31 @@ +package eu.dnetlib.springutils.stringtemplate; + + +import static org.junit.Assert.*; + +import javax.annotation.Resource; + +import org.antlr.stringtemplate.StringTemplate; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Test a string template factory bean. + * @author marko + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class StringTemplateFactoryTest { + + @Resource + private StringTemplate template; + + @Test + public void testStringTemplate() { + assertEquals("check template", "test\n", template.toString()); + } + +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/template/ReorderTemplateTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/template/ReorderTemplateTest.java new file mode 100644 index 0000000..7b1d6d5 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/template/ReorderTemplateTest.java @@ -0,0 +1,30 @@ +package eu.dnetlib.springutils.template; + + +import static org.junit.Assert.*; // NOPMD + +import javax.annotation.Resource; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Templates should work also if parsed out of order (instantiations before declarations). + * + * @author marko + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class ReorderTemplateTest { + + @Resource(name = "test") + private transient TestBean testBean; + + @Test + public void testOutOfOrder() { + assertNotNull(testBean); + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/template/TemplateTest.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/template/TemplateTest.java new file mode 100644 index 0000000..30cead5 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/template/TemplateTest.java @@ -0,0 +1,56 @@ +package eu.dnetlib.springutils.template; + + +import static org.junit.Assert.*; // NOPMD + +import javax.annotation.Resource; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class TemplateTest { + + @Resource(name = "test") + private transient TestBean testBean; + + @Resource(name = "testDefault1") + private transient TestBean testDefault1; + + @Resource(name = "testDefault2") + private transient TestBean testDefault2; + + @Resource(name = "testDefault3") + private transient TestBean testDefault3; + + @Before + public void setUp() throws Exception { + } + + @Test + public void testTemplate() { + assertEquals("check property value expansion", "expandedValue", testBean.getAddress()); + assertEquals("check bean reference", "tail-expandedValue", testBean.getNext().getAddress()); + } + + @Test + public void testMultipleInstances() { + assertEquals("check expanded value", "expandedValue", testDefault1.getAddress()); + assertEquals("check other value", "otherValue", testDefault2.getAddress()); + } + + @Test + public void testDefault() { + assertEquals("check expanded value", "expandedValue", testDefault1.getAddress()); + assertEquals("check default value", "defaultValue", testDefault3.getAddress()); + } + + @Test + public void testProp() { + assertEquals("check property", "test", testBean.getProp()); + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/springutils/template/TestBean.java b/dnet-core-components/src/test/java/eu/dnetlib/springutils/template/TestBean.java new file mode 100644 index 0000000..95b8474 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/springutils/template/TestBean.java @@ -0,0 +1,43 @@ +package eu.dnetlib.springutils.template; + +public class TestBean { + private int value; + + private String address; + + private String prop; + + private TestBean next; + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + public TestBean getNext() { + return next; + } + + public void setNext(TestBean next) { + this.next = next; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/test/utils/EPRTestUtil.java b/dnet-core-components/src/test/java/eu/dnetlib/test/utils/EPRTestUtil.java new file mode 100644 index 0000000..ca96226 --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/test/utils/EPRTestUtil.java @@ -0,0 +1,49 @@ +package eu.dnetlib.test.utils; + +import javax.xml.namespace.QName; +import javax.xml.ws.wsaddressing.W3CEndpointReference; +import javax.xml.ws.wsaddressing.W3CEndpointReferenceBuilder; + +/** + * Test units have to create some EPRs. This class offers static methods for this quick and dirty task. + * + * @author marko + * + */ +public final class EPRTestUtil { + + /** + * coverage tests complain about missing private constructor invocation: here it is. + */ + @SuppressWarnings("unused") + private static EPRTestUtil useless = new EPRTestUtil(); + + /** + * prevents instantiation. + */ + private EPRTestUtil() { + // prevents instantiation + } + + /** + * creates a test epr with some fixed address. + * + * @return a test epr + */ + public static W3CEndpointReference getTestEpr() { + return getTestEpr("http://test"); + } + + /** + * sometimes you may need different eprs. + * + * @param address some fake address + * @return a test epr + */ + public static W3CEndpointReference getTestEpr(final String address) { + final W3CEndpointReferenceBuilder builder = new W3CEndpointReferenceBuilder(); + builder.address(address); + builder.serviceName(new QName("http://something", "myService")); + return builder.build(); + } +} diff --git a/dnet-core-components/src/test/java/eu/dnetlib/test/utils/MockBeanFactory.java b/dnet-core-components/src/test/java/eu/dnetlib/test/utils/MockBeanFactory.java new file mode 100644 index 0000000..e3ba41a --- /dev/null +++ b/dnet-core-components/src/test/java/eu/dnetlib/test/utils/MockBeanFactory.java @@ -0,0 +1,55 @@ +package eu.dnetlib.test.utils; + + +import static org.mockito.Mockito.mock; + +import org.springframework.beans.factory.FactoryBean; + +/** + * Return a mockito mock for a given class. + * This class should be updated according to new Spring4 factory Bean + * + * @author marko + * + */ +@Deprecated +public class MockBeanFactory implements FactoryBean { + + /** + * class to mock. + */ + private Class clazz; + + /** + * {@inheritDoc} + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + public Object getObject() throws Exception { + return mock(clazz); + } + + /** + * {@inheritDoc} + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + public Class getObjectType() { + return clazz; + } + + /** + * {@inheritDoc} + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + public boolean isSingleton() { + return true; + } + + public Class getClazz() { + return clazz; + } + + public void setClazz(final Class clazz) { + this.clazz = clazz; + } + +} diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/enabling/locators/serviceProfile.xml.st b/dnet-core-components/src/test/resources/eu/dnetlib/enabling/locators/serviceProfile.xml.st new file mode 100644 index 0000000..db8a018 --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/enabling/locators/serviceProfile.xml.st @@ -0,0 +1,39 @@ + + +
    + + + + + + + + + +
    + + + + 0 + 0 + 0 + + + + 0 + 0 + + + + + + 0 + 0 + + + + + + + +
    \ No newline at end of file diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/enabling/tools/DynamicServiceLocatorTest-context.xml b/dnet-core-components/src/test/resources/eu/dnetlib/enabling/tools/DynamicServiceLocatorTest-context.xml new file mode 100644 index 0000000..f524cb0 --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/enabling/tools/DynamicServiceLocatorTest-context.xml @@ -0,0 +1,45 @@ + + + + + + + http://localhost:8090/app/services/ + junitTest + true + + + + + + + + + + + + + + + + + + + + 1234http://localhost:1234/app/services/resultSet]]> + 1234http://localhost:8090/otherContext/services/resultSet]]> + 1234http://localhost:8090/app/services/resultSet]]> + 1234http://192.168.1.12:8090/app/services/resultSet]]> + + diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/springutils/aop/MethodInvokingInitializerTest-context.xml b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/aop/MethodInvokingInitializerTest-context.xml new file mode 100644 index 0000000..b27f41a --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/aop/MethodInvokingInitializerTest-context.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/springutils/collections/ResourceReaderCollectionTest-context.xml b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/collections/ResourceReaderCollectionTest-context.xml new file mode 100644 index 0000000..a3a6ebb --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/collections/ResourceReaderCollectionTest-context.xml @@ -0,0 +1,13 @@ + + + + + classpath:/eu/dnetlib/springutils/collections/test.txt + + + diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/springutils/collections/test.txt b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/collections/test.txt new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/collections/test.txt @@ -0,0 +1 @@ +test diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/springutils/condbean/ConditionalBeanTest-context.xml b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/condbean/ConditionalBeanTest-context.xml new file mode 100644 index 0000000..b39f874 --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/condbean/ConditionalBeanTest-context.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/springutils/condbean/ConditionalBeanTest.properties b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/condbean/ConditionalBeanTest.properties new file mode 100644 index 0000000..8df5918 --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/condbean/ConditionalBeanTest.properties @@ -0,0 +1 @@ +some.existing.property = 1 diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/springutils/stringtemplate/StringTemplateFactoryTest-context.xml b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/stringtemplate/StringTemplateFactoryTest-context.xml new file mode 100644 index 0000000..c683998 --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/stringtemplate/StringTemplateFactoryTest-context.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/springutils/stringtemplate/template.txt b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/stringtemplate/template.txt new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/stringtemplate/template.txt @@ -0,0 +1 @@ +test diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/springutils/template/ReorderTemplateTest-context.xml b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/template/ReorderTemplateTest-context.xml new file mode 100644 index 0000000..fbffbc0 --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/template/ReorderTemplateTest-context.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dnet-core-components/src/test/resources/eu/dnetlib/springutils/template/TemplateTest-context.xml b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/template/TemplateTest-context.xml new file mode 100644 index 0000000..a054422 --- /dev/null +++ b/dnet-core-components/src/test/resources/eu/dnetlib/springutils/template/TemplateTest-context.xml @@ -0,0 +1,47 @@ + + + + + + + test + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 4e62568..812bddd 100644 --- a/pom.xml +++ b/pom.xml @@ -76,12 +76,30 @@ org.apache.commons commons-lang3 - 3.5 + ${commons.lang3.version} + + commons-lang + commons-lang + ${commons.lang.version} + + commons-codec commons-codec - 1.9 + ${commons.codec.version} + + + + commons-io + commons-io + ${commons.io.version} + + + + com.mycila + xmltool + 3.3 dom4j @@ -116,13 +134,12 @@ com.google.guava guava - 23.3-jre + ${google.guava.version} - org.mockito - mockito-core - 1.6 - test + com.google.code.gson + gson + ${google.gson.version} junit @@ -130,21 +147,189 @@ 4.9 test + + + + log4j + log4j + ${log4j.version} + + + com.sun.jmx + jmxri + + + com.sun.jdmk + jmxtools + + + + + + runcc + runcc + 0.7 + + + org.antlr + stringtemplate + 3.2 + + + jparsec + jparsec + 2.0 + + + apache + oro + 2.0.8 + + + + + org.quartz-scheduler + quartz + ${quartz.version} + + + + org.apache.solr + solr-solrj + ${apache.solr.version} + + + wstx-asl + org.codehaus.woodstox + + + jcl-over-slf4j + org.slf4j + + + + + + org.apache.lucene + lucene-queryparser + ${apache.solr.version} + + + org.mongodb + mongo-java-driver + ${mongodb.driver.version} + + + joda-time + joda-time + [2.3,3.0) + + + org.z3950.zing + cql-java + 1.7 + + + + org.mockito + mockito-core + ${mockito.version} + test + + + + + org.springframework + spring-beans + ${spring.version} + + + org.springframework + spring-web + ${spring.version} + org.springframework spring-test - [4.2.5.RELEASE] + ${spring.version} false - commons-io - commons-io - 2.5 - test + org.springframework + spring-aop + ${spring.version} + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework + spring-tx + ${spring.version} + + + + + + org.apache.cxf + cxf-core + ${cxf.version} + + + org.apache.cxf + cxf-rt-bindings-soap + ${cxf.version} + + + org.apache.cxf + cxf-rt-transports-http + ${cxf.version} + + + org.apache.cxf + cxf-rt-frontend-jaxws + ${cxf.version} + + + + + + javax.servlet + javax.servlet-api + ${javax.servlet.version} + provided + + + + + org.apache.tomcat + tomcat-catalina + 7.0.47 + provided + + + + [4.2.5.RELEASE] + [3.1.5] + 2.3.7 + 2.6 + 3.5 + 2.4 + 1.9 + 7.5.0 + 3.4.2 + 23.3-jre + 2.2.2 + 2.2.2 + 1.2.17 + [3.1.0] + +