diff --git a/dnet-core-components/src/test/java/eu/dnetlib/miscutils/iterators/xml/IterableXmlParserTest.java b/dnet-core-components/src/test/java/eu/dnetlib/miscutils/iterators/xml/IterableXmlParserTest.java index 080888d..3fdd347 100644 --- a/dnet-core-components/src/test/java/eu/dnetlib/miscutils/iterators/xml/IterableXmlParserTest.java +++ b/dnet-core-components/src/test/java/eu/dnetlib/miscutils/iterators/xml/IterableXmlParserTest.java @@ -16,10 +16,14 @@ import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.io.SAXReader; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +@Ignore +// TODO investigate why it takes so long to run +// Time elapsed: 303.806 sec - in eu.dnetlib.miscutils.iterators.xml.IterableXmlParserTest public class IterableXmlParserTest { private Resource xmlZip = new ClassPathResource("eu/dnetlib/miscutils/iterators/xml/opendoar.zip"); diff --git a/dnet-core-services/pom.xml b/dnet-core-services/pom.xml new file mode 100644 index 0000000..59a7bfe --- /dev/null +++ b/dnet-core-services/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + eu.dnetlib + dnet-core + 1.0.0-SNAPSHOT + + dnet-core-services + eu.dnetlib + jar + 1.0.0-SNAPSHOT + + + + eu.dnetlib + dnet-core-components + ${project.version} + + + + diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/HostingContextManagerServiceImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/HostingContextManagerServiceImpl.java new file mode 100644 index 0000000..6d764bc --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/HostingContextManagerServiceImpl.java @@ -0,0 +1,61 @@ +package eu.dnetlib.enabling.hcm; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.hcm.rmi.HostingContextManagerService; +import eu.dnetlib.enabling.tools.AbstractBaseService; +import eu.dnetlib.enabling.tools.blackboard.NotificationHandler; + +/** + * CNR HostingContextManagerService implementation. Will conflict with NKUA! yes this is ok. + * + * @author marko + * + */ +public class HostingContextManagerServiceImpl extends AbstractBaseService implements HostingContextManagerService { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(HostingContextManagerServiceImpl.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * notification handler. + */ + private NotificationHandler notificationHandler; // NOPMD + + @Override + public void notify(String subscrId, String topic, String isId, String message) { + if (log.isDebugEnabled()) { + log.debug("---- service got notification ----"); + log.debug("subscrId: " + subscrId); + log.debug("topic " + topic); + log.debug("isId " + isId); + log.debug("msg: " + message); + log.debug("____ now processing the notification ____"); + } + getNotificationHandler().notified(subscrId, topic, isId, message); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.tools.AbstractBaseService#start() + */ + @Override + public void start() { + log.info("staring hosting context manager"); + } + + public NotificationHandler getNotificationHandler() { + return notificationHandler; + } + + @Required + public void setNotificationHandler(NotificationHandler notificationHandler) { + this.notificationHandler = notificationHandler; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/sn/HCMSubscriber.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/sn/HCMSubscriber.java new file mode 100644 index 0000000..c1d2d39 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/sn/HCMSubscriber.java @@ -0,0 +1,17 @@ +package eu.dnetlib.enabling.hcm.sn; + +import eu.dnetlib.enabling.is.sn.rmi.ISSNException; + +/** + * This component takes care of subscribing the MSRO service to interesting events. + * + * @author marko + * + */ +public interface HCMSubscriber { + /** + * performs the subscription. + * @throws ISSNException could happen + */ + void subscribeAll() throws ISSNException; +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/sn/HCMSubscriberImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/sn/HCMSubscriberImpl.java new file mode 100644 index 0000000..7fc9395 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/sn/HCMSubscriberImpl.java @@ -0,0 +1,117 @@ +package eu.dnetlib.enabling.hcm.sn; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import javax.xml.ws.Endpoint; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.actions.SubscriptionAction; +import eu.dnetlib.enabling.is.sn.rmi.ISSNException; +import eu.dnetlib.enabling.is.sn.rmi.ISSNService; +import eu.dnetlib.enabling.locators.UniqueServiceLocator; +import eu.dnetlib.soap.EndpointReferenceBuilder; + +/** + * This component takes care of subscribing the SubscriptionAction(s) to interesting events. + * + * @author claudio + * + */ +public class HCMSubscriberImpl implements HCMSubscriber { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(HCMSubscriberImpl.class); + + /** + * notification endpoint (normally the msro service). + */ + private Endpoint endpoint; + + /** + * service locator. + */ + private UniqueServiceLocator serviceLocator; + + /** + * injected EPR builder. + */ + @Resource(name = "jaxwsEndpointReferenceBuilder") + private EndpointReferenceBuilder eprBuilder; + + /** + * subscription actions. + */ + private List actions; + + @PostConstruct + public void printList() { + log.info(getActions()); + } + + /** + * {@inheritDoc} + * + * @throws ISSNException + * could happen + * @see eu.dnetlib.enabling.hcm.sn.HCMSubscriber#subscribeAll() + */ + @Override + public void subscribeAll() throws ISSNException { + + final W3CEndpointReference endpointReference = eprBuilder + .getEndpointReference(getEndpoint()); + + if (getActions() != null) { + for (final SubscriptionAction action : getActions()) { + log.info("dynamically subscribing to " + + action.getTopicExpression()); + serviceLocator.getService(ISSNService.class, true).subscribe(endpointReference, + action.getTopicExpression(), 0); + } + } + } + + public void setEndpoint(final Endpoint endpoint) { + this.endpoint = endpoint; + } + + public Endpoint getEndpoint() { + return endpoint; + } + + public void setEprBuilder( + final EndpointReferenceBuilder eprBuilder) { + this.eprBuilder = eprBuilder; + } + + public EndpointReferenceBuilder getEprBuilder() { + return eprBuilder; + } + + public List getActions() { + return actions; + } + + @Required + public void setActions(final List actions) { + this.actions = actions; + } + + public UniqueServiceLocator getServiceLocator() { + return serviceLocator; + } + + @Required + public void setServiceLocator(final UniqueServiceLocator serviceLocator) { + this.serviceLocator = serviceLocator; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/sn/HCMSubscriptionListFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/sn/HCMSubscriptionListFactory.java new file mode 100644 index 0000000..eece859 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/hcm/sn/HCMSubscriptionListFactory.java @@ -0,0 +1,57 @@ +package eu.dnetlib.enabling.hcm.sn; + +import java.util.List; + +import com.google.common.collect.Lists; +import eu.dnetlib.enabling.actions.SubscriptionAction; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * This class acts as a simple registry of msro subscription actions. + * + *

+ * Subscription actions are autodetected using spring autowiring from the current application context and are available for the manager for + * handling subscriptions and delivering notification to particular code interested in those notifications. + *

+ * + * @author marko + * + */ +@SuppressWarnings("rawtypes") +public class HCMSubscriptionListFactory implements FactoryBean> { + + /** + * actions. Spring injects all the declared SubscriptionActions here. + */ + @Autowired(required = false) + private List actions = Lists.newArrayList(); + + public List getActions() { + return actions; + } + + public void setActions(final List actions) { + this.actions = actions; + } + + /** + * {@inheritDoc} + * + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + @Override + public List getObject() { + return getActions(); + } + + @Override + public Class getObjectType() { + return List.class; + } + + @Override + public boolean isSingleton() { + return false; + } +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/hnm/HostingNodeManagerServiceImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/hnm/HostingNodeManagerServiceImpl.java new file mode 100644 index 0000000..d3663ae --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/hnm/HostingNodeManagerServiceImpl.java @@ -0,0 +1,19 @@ +package eu.dnetlib.enabling.hnm; + +import eu.dnetlib.enabling.hnm.rmi.HostingNodeManagerService; +import eu.dnetlib.enabling.tools.AbstractBaseService; + +/** + * HNMService implementation. + * + * @author michele + * + */ +public class HostingNodeManagerServiceImpl extends AbstractBaseService implements HostingNodeManagerService { + + @Override + public String echo(String s) { + return s; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/AbstractObservableResultset.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/AbstractObservableResultset.java new file mode 100644 index 0000000..68fa657 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/AbstractObservableResultset.java @@ -0,0 +1,155 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.Map; +import java.util.WeakHashMap; + +import eu.dnetlib.enabling.resultset.observer.DelegationObserver; +import eu.dnetlib.enabling.resultset.observer.Observable; + + +/** + * Common stuff for resultset implementations bound to a ResultSetRegistry. + * + * @author marko + * + */ +public abstract class AbstractObservableResultset implements ResultSet, Observable { + /** + * true if the resultset is destroyed. + */ + private boolean destroyed = false; + + /** + * true if the resultset is open. + */ + private boolean open; + + /** + * delegate the real job to a java.util.Observable object. + */ + private transient final java.util.Observable observable = new DelegationObservable(); + + /** + * weak map for removing single observers later on. + */ + private transient Map observers = new WeakHashMap(); + + /** + * java.util.Observable requires a "change" in the object to be made for the notifyObservers methods to trigger + * actually the notification. We don't think this should be part of the base Observable/Observer pattern and thus + * hide this fact. If an user of AbstractObservable wants this behavior it should implement her own change tracking + * and avoid calling notifyObservers altogether. The rationale behind this choice is that not all dirty states can + * be expressed using a stateful boolean instance variable like that implemented by java.util.Observable; some users + * may prefer/need a lazy dirty flag instead. + * + * @author marko + * + */ + static class DelegationObservable extends java.util.Observable { + + /** + * {@inheritDoc} + * + * @see java.util.Observable#notifyObservers() + */ + @Override + public void notifyObservers() { + setChanged(); + super.notifyObservers(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Observable#notifyObservers(java.lang.Object) + */ + @Override + public void notifyObservers(final Object arg) { + setChanged(); + super.notifyObservers(arg); + } + + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.ResultSet#destroy() + */ + @Override + public void destroy() { + if (!isDestroyed()) { + setDestroyed(true); + notifyObservers(); + deleteObservers(); + } + } + + public void setDestroyed(final boolean destroyed) { + this.destroyed = destroyed; + } + + @Override + public boolean isDestroyed() { + return destroyed; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.ResultSet#isOpen() + */ + @Override + public boolean isOpen() { + return open; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.ResultSet#close() + */ + @Override + public void close() { + open = false; + } + + public void setOpen(final boolean open) { + this.open = open; + } + + @Override + public void addObserver(final ResultSetRegistry observer) { + final DelegationObserver delegate = new DelegationObserver(this, observer); + observers.put(observer, delegate); + observable.addObserver(delegate); + } + + protected void notifyObservers() { + observable.notifyObservers(); + } + + protected void notifyObservers(final Object arg) { + observable.notifyObservers(arg); + } + + public int countObservers() { + return observable.countObservers(); + } + + public void deleteObserver(final java.util.Observer observer) { + observable.deleteObserver(observer); + } + + public void deleteObserver(ResultSetRegistry observer) { + DelegationObserver delegate = observers.get(observer); + if(delegate != null) { + observable.deleteObserver(delegate); + } + } + + public void deleteObservers() { + observable.deleteObservers(); + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/AbstractResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/AbstractResultSetFactory.java new file mode 100644 index 0000000..88ef504 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/AbstractResultSetFactory.java @@ -0,0 +1,38 @@ +package eu.dnetlib.enabling.resultset; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * Resultset factories bound to a resultset service, from which it takes the resultset registry and the epr builder. + * + * @author marko + * + */ +public abstract class AbstractResultSetFactory implements ResultSetFactory { + + /** + * resultset service. + */ + private ResultSetServiceImpl resultSetService; + + /** + * Register a resultset instance to the underlying resultset registry and returns + * and EPR to accessible through the resultset service. + * + * @param resultSet resultset instance + * @return epr to the newly created resultset + */ + public W3CEndpointReference registerResultSet(final ResultSet resultSet) { + resultSetService.getResultsetRegistry().addResultSet(resultSet); + return resultSetService.getEprBuilder().getEndpointReference(resultSetService.getEndpoint(), resultSet.getIdentifier()); + } + + public ResultSetServiceImpl getResultSetService() { + return resultSetService; + } + + public void setResultSetService(final ResultSetServiceImpl resultSetService) { + this.resultSetService = resultSetService; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CachingResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CachingResultSetFactory.java new file mode 100644 index 0000000..a854363 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CachingResultSetFactory.java @@ -0,0 +1,46 @@ +package eu.dnetlib.enabling.resultset; + +import javax.annotation.Resource; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.tools.ServiceResolver; + +public class CachingResultSetFactory { + /** + * underlying resultset factory, which exposes local resultsets to the world. + */ + private ResultSetFactory resultSetFactory; + + @Resource + private ServiceResolver serviceResolver; + + /** + * @param items + * @param size + * the size of the iterable + * @return + */ + public W3CEndpointReference createCachingResultSet(W3CEndpointReference epr) { + return resultSetFactory.createResultSet(new CachingResultSetListener(epr, serviceResolver)); + } + + public ResultSetFactory getResultSetFactory() { + return resultSetFactory; + } + + @Required + public void setResultSetFactory(ResultSetFactory resultSetFactory) { + this.resultSetFactory = resultSetFactory; + } + + public ServiceResolver getServiceResolver() { + return serviceResolver; + } + + public void setServiceResolver(ServiceResolver serviceResolver) { + this.serviceResolver = serviceResolver; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CachingResultSetListener.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CachingResultSetListener.java new file mode 100644 index 0000000..ab512d5 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CachingResultSetListener.java @@ -0,0 +1,61 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import eu.dnetlib.enabling.resultset.rmi.ResultSetException; +import eu.dnetlib.enabling.resultset.rmi.ResultSetService; +import eu.dnetlib.enabling.tools.ServiceResolver; + +public class CachingResultSetListener implements ResultSetListener { + private static final Log log = LogFactory.getLog(CachingResultSetListener.class); // NOPMD by marko on 11/24/08 5:02 PM + + + private ResultSetService service; + private String rsId; + + private ArrayList storage = new ArrayList(); + + public CachingResultSetListener(W3CEndpointReference epr, ServiceResolver serviceResolver) { + this.service = serviceResolver.getService(ResultSetService.class, epr); + this.rsId = serviceResolver.getResourceIdentifier(epr); + } + + @Override + public List getResult(int fromPosition, int toPosition) { + if (storage.get(fromPosition) != null) + return cached(fromPosition, toPosition); + try { + List data = service.getResult(rsId, fromPosition, toPosition, "waiting"); + cache(data, fromPosition, toPosition); + return data; + } catch (ResultSetException e) { + throw new IllegalStateException(e); + } + } + + private void cache(List data, int fromPosition, int toPosition) { + for(String value : data) + storage.set(fromPosition++, value); + } + + private List cached(int fromPosition, int toPosition) { + log.info("found cached page " + fromPosition + ", " + toPosition); + return storage.subList(fromPosition-1, toPosition); + } + + @Override + public int getSize() { + try { + return service.getNumberOfElements(rsId); + } catch (ResultSetException e) { + throw new IllegalStateException(e); + } + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CountingResultSet.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CountingResultSet.java new file mode 100644 index 0000000..cc66032 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CountingResultSet.java @@ -0,0 +1,60 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.List; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.enabling.tools.ServiceResolver; +import eu.dnetlib.miscutils.functional.IdentityFunction; +import eu.dnetlib.miscutils.functional.UnaryFunction; + +/** + * A resultset record counter. + * + * @author claudio + * + */ +public class CountingResultSet extends MappedResultSet { + + /** + * counter. + */ + private int count; + + + /** + * @param epr + * @param serviceResolver + */ + public CountingResultSet(final W3CEndpointReference epr, final ServiceResolver serviceResolver) { + this(epr, new IdentityFunction(), serviceResolver); + } + + /** + * @param epr + * @param mapper + * @param serviceResolver + */ + public CountingResultSet(final W3CEndpointReference epr, final UnaryFunction mapper, final ServiceResolver serviceResolver) { + super(epr, mapper, serviceResolver); + count = 0; + } + + /** + * method sets the counter to the higher allowed value of toPosition. + */ + @Override + public List getResult(int fromPosition, int toPosition) { + + if (toPosition > count) + count = toPosition; + if (toPosition > super.getSize()) + count = super.getSize(); + + return super.getResult(fromPosition, toPosition); + } + + public int getCount() { + return count; + } +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CountingResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CountingResultSetFactory.java new file mode 100644 index 0000000..cbe6491 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/CountingResultSetFactory.java @@ -0,0 +1,26 @@ +package eu.dnetlib.enabling.resultset; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * Create a new resultset which takes each record of the input resultset and counts them. + * + * @author claudio + * + */ +public class CountingResultSetFactory extends MappedResultSetFactory { + + /** + * Create a new resultset which takes each record of the input resultset and counts them. + * + * @param source + * source resultset epr + * @param mapper + * mapper function + * @return mapped resultset epr + */ + public W3CEndpointReference createCountingResultSet(final W3CEndpointReference source) { + return getResultSetFactory().createResultSet(new CountingResultSet(source, getServiceResolver())); + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/EPRPoolingLocalOpenResultSetFactoryImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/EPRPoolingLocalOpenResultSetFactoryImpl.java new file mode 100644 index 0000000..c92cde1 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/EPRPoolingLocalOpenResultSetFactoryImpl.java @@ -0,0 +1,23 @@ +package eu.dnetlib.enabling.resultset; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.springframework.beans.factory.annotation.Required; + +public class EPRPoolingLocalOpenResultSetFactoryImpl extends LocalOpenResultSetFactoryImpl { + private ResultSetEPRPool pool; + + @Override + public W3CEndpointReference registerResultSet(ResultSet resultSet) { + return pool.registerResultSet(resultSet); + } + + public ResultSetEPRPool getPool() { + return pool; + } + + @Required + public void setPool(ResultSetEPRPool pool) { + this.pool = pool; + } +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/EPRPoolingLocalResultSetFactoryImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/EPRPoolingLocalResultSetFactoryImpl.java new file mode 100644 index 0000000..f5f62df --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/EPRPoolingLocalResultSetFactoryImpl.java @@ -0,0 +1,27 @@ +package eu.dnetlib.enabling.resultset; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.springframework.beans.factory.annotation.Required; + +public class EPRPoolingLocalResultSetFactoryImpl extends LocalResultSetFactoryImpl { + + private ResultSetEPRPool pool; + + @Override + public W3CEndpointReference registerResultSet(ResultSet resultSet) { + return pool.registerResultSet(resultSet); + } + + public ResultSetEPRPool getPool() { + return pool; + } + + @Required + public void setPool(ResultSetEPRPool pool) { + this.pool = pool; + } + + + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/FetchList.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/FetchList.java new file mode 100644 index 0000000..3c7f167 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/FetchList.java @@ -0,0 +1,105 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.Iterator; +import java.util.LinkedList; + +/** + * a LinkedList populated by a given iterable, which provides a fetch window of elements + * + * @author claudio + * + * @param + * The FetchList type + */ +public class FetchList extends LinkedList { + + /** + * + */ + private static final long serialVersionUID = 7135272008563693321L; + + /** + * represents the number of elements ready to be pulled out of the list + */ + private int fetchSize; + + /** + * represents the number of elements already consumed by a poll call + */ + private int consumedElements; + + /** + * the given itarable used to populate the list + */ + private Iterator iter; + + /** + * @param iter + * @param fetchSize + */ + public FetchList(final Iterator iter, final int fetchSize) { + super(); + this.consumedElements = 0; + this.fetchSize = fetchSize; + this.iter = iter; + } + + /** + * used to fill the list + */ + public synchronized void fill() { + for (int i = 0; i < fetchSize; i++) { + if (!iter.hasNext()) { + break; + } + this.add(iter.next()); + } + } + + /** + * return the first element and makes the list to be filled in case its size is zero + */ + @Override + public T poll() { + if (this.isEmpty()) { + fill(); + } + + if (this.size() > 0) { + consumedElements++; + return super.poll(); + } + return null; + } + + /** + * @return the number of elements already consumed by a "poll" + */ + public int getConsumedElements() { + return consumedElements; + } + + /** + * @return the actual number of elements of the list + */ + public int getTotalElements() { + return consumedElements + this.size(); + } + + /** + * to provide an actual size, fills the list if there are elements to fill it + */ + @Override + public int size() { + if (this.isEmpty() && iter.hasNext()) { + fill(); + } + return super.size(); + } + + @Override + public boolean isEmpty() { + return super.size() == 0; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/IterableResultSet.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/IterableResultSet.java new file mode 100644 index 0000000..d6b808b --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/IterableResultSet.java @@ -0,0 +1,116 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * an open ResultSet that can be iterated to get it's data results. + * + * @author claudio + * + */ +public class IterableResultSet implements ResultSetListener, ResultSetAware { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(IterableResultSet.class); + + /** + * the container for the data results. + */ + private ResultSet resultSet; + + /** + * the list that provides data for this resultSet. + */ + private FetchList fetchList; + + private int optionalNumberOfElements; + + /** + * + * @param iter + * @param fetchSize + */ + protected IterableResultSet(Iterable iter, int fetchSize) { + fetchList = new FetchList(iter.iterator(), fetchSize); + if (iter instanceof SizedIterable) { + optionalNumberOfElements = ((SizedIterable) iter).getNumberOfElements(); + } else { + optionalNumberOfElements = -1; + } + } + + /** + * @param fromPosition + * @param toPosition + * @return List + */ + @Override + public List getResult(int fromPosition, int toPosition) { + + log.debug(" - getting result from " + fromPosition + " to " + toPosition + ", consumedElements: " + fetchList.getConsumedElements()); + + if (fromPosition != fetchList.getConsumedElements() + 1) + throw new RuntimeException("Unexpected value for 'fromPosition' parameter"); + if (toPosition < fromPosition) + throw new RuntimeException("'fromPosition' must be lower or equal than 'toPosition'"); + + List result = new ArrayList(); + + for (int i = fromPosition; i <= toPosition; i++) { + if (fetchList.size() > 0) + result.add(fetchList.poll()); + else + break; + } + + ensureClosed(); + + return result; + } + + private void ensureClosed() { + if (fetchList.size() == 0 && resultSet.isOpen()) { + log.info(">>>>>>>>>>> closing resultset <<<<<<<<<<<<<"); + close(); + } + } + + public String getRSStatus() { + if (resultSet.isOpen()) + return "open"; + return "closed"; + } + + @Override + public int getSize() { + ensureClosed(); + + if (optionalNumberOfElements == -1) + return fetchList.getTotalElements(); + else + return optionalNumberOfElements; + } + + /** + * closes the resultset. + */ + protected void close() { + resultSet.close(); + } + + @Override + public void setResultSet(ResultSet resultSet) { + this.resultSet = resultSet; + } + + public ResultSet getResultSet() { + return resultSet; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/IterableResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/IterableResultSetFactory.java new file mode 100644 index 0000000..8bceff2 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/IterableResultSetFactory.java @@ -0,0 +1,67 @@ +package eu.dnetlib.enabling.resultset; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.springframework.beans.factory.annotation.Required; + +/** + * + * @author claudio + * + */ +public class IterableResultSetFactory { + + /** + * {@link ResultSetFactory} + */ + private LocalOpenResultSetFactoryImpl resultSetFactory; + + /** + * + */ + private int fetchSize; + + /** + * + * @param source + * @return + */ + public W3CEndpointReference createIterableResultSet(final Iterable source) { + return resultSetFactory.createResultSet(new IterableResultSet(source, fetchSize)); + } + + /** + * + * @return + */ + public LocalOpenResultSetFactoryImpl getResultSetFactory() { + return resultSetFactory; + } + + /** + * + * @param resultSetFactory + */ + @Required + public void setResultSetFactory(final LocalOpenResultSetFactoryImpl resultSetFactory) { + this.resultSetFactory = resultSetFactory; + } + + /** + * + * @param fetchSize + */ + @Required + public void setFetchSize(int fetchSize) { + this.fetchSize = fetchSize; + } + + /** + * + * @return + */ + public int getFetchSize() { + return fetchSize; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/JaxbResultsetListenerWrapper.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/JaxbResultsetListenerWrapper.java new file mode 100644 index 0000000..516dbb3 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/JaxbResultsetListenerWrapper.java @@ -0,0 +1,68 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.List; + +import javax.xml.bind.JAXBException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.google.common.collect.Lists; + +import eu.dnetlib.miscutils.collections.MappedCollection; +import eu.dnetlib.miscutils.functional.UnaryFunction; +import eu.dnetlib.miscutils.jaxb.JaxbFactory; + +/** + * Given a jaxb factory for a given type, wraps TypedResultsetListeners to ResultSetListeners, serializing objects + * according to the jaxb factory. + * + * @author marko + * + * @param + */ +public class JaxbResultsetListenerWrapper { + private static final Log log = LogFactory.getLog(JaxbResultsetListenerWrapper.class); // NOPMD by marko on 11/24/08 5:02 PM + + private JaxbFactory factory; + + /** + * Returns a listener which serializes each item according to the jaxb factory. + * + * @param listener + * listener which returns T + * @return listener which returns strings + */ + public ResultSetListener wrap(final TypedResultSetListener listener) { + return new ResultSetListener() { + + @Override + public List getResult(final int fromPosition, final int toPosition) { + return Lists.newArrayList(new MappedCollection(listener.getResult(fromPosition, toPosition), new UnaryFunction() { + @Override + public String evaluate(final T value) { + try { + return factory.serialize(value); + } catch (final JAXBException e) { + log.warn("cannot serialize", e); + throw new RuntimeException(e); + } + } + })); + } + + @Override + public int getSize() { + return listener.getSize(); + } + }; + } + + public JaxbFactory getFactory() { + return factory; + } + + public void setFactory(final JaxbFactory factory) { + this.factory = factory; + } +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/LocalOpenResultSetFactoryImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/LocalOpenResultSetFactoryImpl.java new file mode 100644 index 0000000..96b81bd --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/LocalOpenResultSetFactoryImpl.java @@ -0,0 +1,23 @@ +package eu.dnetlib.enabling.resultset; + +/** + * Creates an "open" (unbounded, unfreezed) local resultset. + * + * @author marko + * + */ +public class LocalOpenResultSetFactoryImpl extends LocalResultSetFactoryImpl { + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.resultset.LocalResultSetFactoryImpl#createInstance(eu.dnetlib.enabling.resultset.ResultSetListener) + */ + @Override + protected LocalResultSetImpl createInstance(final ResultSetListener provider) { + final LocalResultSetImpl instance = super.createInstance(provider); + instance.setOpen(true); + return instance; + } + + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/LocalResultSetFactoryImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/LocalResultSetFactoryImpl.java new file mode 100644 index 0000000..254f444 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/LocalResultSetFactoryImpl.java @@ -0,0 +1,38 @@ +package eu.dnetlib.enabling.resultset; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * This factory creates resultset bound to a given resultset registry shared with a resultset service which exposed them + * to the outside world. + * + * It's tightly coupled with a local instance of the ResultSetService. + * + * @see eu.dnetlib.enabling.resultset.ResultSetServiceImpl + * + * @author marko + * + */ +public class LocalResultSetFactoryImpl extends AbstractResultSetFactory { + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.ResultSetFactory#createResultSet(eu.dnetlib.enabling.resultset.ResultSetListener) + */ + @Override + public W3CEndpointReference createResultSet(final ResultSetListener provider) { + return registerResultSet(createInstance(provider)); + } + + /** + * create a new local resultset instance. Subclasses may override this to supply additional configuration. + * + * @param provider provider + * @return a new resultset instance. + */ + protected LocalResultSetImpl createInstance(final ResultSetListener provider) { + return new LocalResultSetImpl(provider); + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/LocalResultSetImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/LocalResultSetImpl.java new file mode 100644 index 0000000..e36a85b --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/LocalResultSetImpl.java @@ -0,0 +1,98 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.List; + +import com.google.common.collect.Lists; + +/** + * Resultset backing implementation. + * + * @author marko + * + */ +public class LocalResultSetImpl extends AbstractObservableResultset implements ResultSet { + + /** + * rsId. + */ + private String identifier; + + /** + * this listener will provide the data to this local resultset by listening to events. + */ + private ResultSetListener listener; + + /** + * Construct a new local ResultSet which pulls data from the listener. If the listener implements ResultSetAware, it + * will be injected with this instance. + * + * @param listener + * a resultset listener + */ + public LocalResultSetImpl(final ResultSetListener listener) { + super(); + this.listener = listener; + + if (listener instanceof ResultSetAware) + ((ResultSetAware) listener).setResultSet(this); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.ResultSet#getNumberOfResults() + */ + @Override + public int getNumberOfResults() { + return listener.getSize(); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.ResultSet#getResults(int, int) + */ + @Override + public List getResults(final int fromPosition, final int toPosition) { + int toPos = toPosition; + int fromPos = fromPosition; + if (!isOpen()) { + final int size = getNumberOfResults(); + + if (size == 0) + return Lists.newArrayList(); + + if (fromPos > size) + return Lists.newArrayList(); + + if (toPos > size) + toPos = size; + } + + if (fromPos < 1) + fromPos = 1; + if (toPos < fromPos) + toPos = fromPos; + + return listener.getResult(fromPos, toPos); + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public void setIdentifier(final String identifier) { + this.identifier = identifier; + } + + public ResultSetListener getListener() { + return listener; + } + + public void setListener(final ResultSetListener listener) { + this.listener = listener; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/MappedResultSet.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/MappedResultSet.java new file mode 100644 index 0000000..1f65358 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/MappedResultSet.java @@ -0,0 +1,151 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.List; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.resultset.rmi.ResultSetException; +import eu.dnetlib.enabling.resultset.rmi.ResultSetService; +import eu.dnetlib.enabling.tools.ServiceResolver; +import eu.dnetlib.miscutils.collections.MappedCollection; +import eu.dnetlib.miscutils.functional.UnaryFunction; + +/** + * A resultset filter. Applies a transformation for every resultset record. + * + * @author marko + * + */ +public class MappedResultSet implements ResultSetListener, ResultSetAware { + + /** + * input resultset. + */ + private ResultSetService resultSetService; + + /** + * input resultset id. + */ + private String rsId; + + /** + * mapper function. + */ + private UnaryFunction mapper; + + /** + * service resolver. + */ + private ServiceResolver serviceResolver; + + private ResultSet resultSet; + + /** + * Create a new mapped resultset. + * + * @param epr + * input resultset epr + * @param mapper + * mapper function + * @param serviceResolver + * service resolver + */ + public MappedResultSet(final W3CEndpointReference epr, final UnaryFunction mapper, final ServiceResolver serviceResolver) { + super(); + + this.resultSetService = serviceResolver.getService(ResultSetService.class, epr); + this.rsId = serviceResolver.getResourceIdentifier(epr); + + this.mapper = mapper; + + this.serviceResolver = serviceResolver; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.ResultSetListener#getResult(int, int) + */ + @Override + public List getResult(final int fromPosition, final int toPosition) { + return MappedCollection.listMap(getResultFromSource(fromPosition, toPosition), mapper); + + } + + protected List getResultFromSource(final int fromPosition, final int toPosition) { + try { + List res = resultSetService.getResult(rsId, fromPosition, toPosition, "waiting"); + checkStatus(); + return res; + } catch (final ResultSetException e) { + throw new IllegalStateException(e); + } + } + + private void checkStatus() throws ResultSetException { + if (getResultSet().isOpen() && resultSetService.getRSStatus(rsId).equals("closed")) { + getResultSet().close(); + } + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.ResultSetListener#getSize() + */ + @Override + public int getSize() { + try { + int numberOfElements = resultSetService.getNumberOfElements(rsId); + checkStatus(); + return numberOfElements; + } catch (final ResultSetException e) { + throw new IllegalStateException(e); + } + } + + public ResultSetService getResultSetService() { + return resultSetService; + } + + public void setResultSetService(final ResultSetService resultSetService) { + this.resultSetService = resultSetService; + } + + public UnaryFunction getMapper() { + return mapper; + } + + public void setMapper(final UnaryFunction mapper) { + this.mapper = mapper; + } + + public ServiceResolver getServiceResolver() { + return serviceResolver; + } + + @Required + public void setServiceResolver(final ServiceResolver serviceResolver) { + this.serviceResolver = serviceResolver; + } + + @Override + public void setResultSet(ResultSet resultSet) { + this.resultSet = resultSet; + } + + public ResultSet getResultSet() { + return resultSet; + } + + public String getRsId() { + return rsId; + } + + public void setRsId(String rsId) { + this.rsId = rsId; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/MappedResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/MappedResultSetFactory.java new file mode 100644 index 0000000..dc61532 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/MappedResultSetFactory.java @@ -0,0 +1,53 @@ +package eu.dnetlib.enabling.resultset; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.enabling.tools.ServiceResolver; +import eu.dnetlib.miscutils.functional.UnaryFunction; + +/** + * Create a new resultset which takes each record of the input resultset and applies the mapping function to it. + * + * @author marko + * + */ +public class MappedResultSetFactory { + + /** + * underlying resultset factory, which exposes local resultsets to the world. + */ + private ResultSetFactory resultSetFactory; + + /** + * service resolver, transforms eprs to services. + */ + private ServiceResolver serviceResolver; + + /** + * Create a new resultset which takes each record of the input resultset and applies the mapping function to it. + * + * @param source source resultset epr + * @param mapper mapper function + * @return mapped resultset epr + */ + public W3CEndpointReference createMappedResultSet(final W3CEndpointReference source, final UnaryFunction mapper) { + return resultSetFactory.createResultSet(new MappedResultSet(source, mapper, serviceResolver)); + } + + public ResultSetFactory getResultSetFactory() { + return resultSetFactory; + } + + public void setResultSetFactory(final ResultSetFactory resultSetFactory) { + this.resultSetFactory = resultSetFactory; + } + + public ServiceResolver getServiceResolver() { + return serviceResolver; + } + + public void setServiceResolver(final ServiceResolver serviceResolver) { + this.serviceResolver = serviceResolver; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ParallelMappedResultSet.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ParallelMappedResultSet.java new file mode 100644 index 0000000..75d70ac --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ParallelMappedResultSet.java @@ -0,0 +1,57 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import com.google.common.collect.Lists; + +import eu.dnetlib.enabling.tools.ServiceResolver; +import eu.dnetlib.miscutils.collections.MappedCollection; +import eu.dnetlib.miscutils.functional.UnaryFunction; + +public class ParallelMappedResultSet extends MappedResultSet { + + private ExecutorService executor; + + public ParallelMappedResultSet(final W3CEndpointReference source, final UnaryFunction mapper, final ServiceResolver serviceResolver, + final ExecutorService executor) { + super(source, mapper, serviceResolver); + this.executor = executor; + } + + @Override + public List getResult(int fromPosition, int toPosition) { + +// log.info("Parallel page " + (toPosition - fromPosition + 1)); + + List> results = Lists.newArrayList(); + + for (final String input : getResultFromSource(fromPosition, toPosition)) { + results.add(executor.submit(new Callable() { + @Override + public String call() throws Exception { + return getMapper().evaluate(input); + } + })); + } + + return MappedCollection.listMap(results, new UnaryFunction>() { + @Override + public String evaluate(Future arg) { + try { + return arg.get(); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } catch (ExecutionException e) { + throw new IllegalStateException(e); + } + } + }); + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ParallelMappedResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ParallelMappedResultSetFactory.java new file mode 100644 index 0000000..1191fa5 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ParallelMappedResultSetFactory.java @@ -0,0 +1,49 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.miscutils.functional.UnaryFunction; + +public class ParallelMappedResultSetFactory extends MappedResultSetFactory { + + private ExecutorService executor; + + private static final int QUEUE_SIZE = 40; + + private int queueSize = QUEUE_SIZE; + + private int cpus = 0; + + public ParallelMappedResultSetFactory() { + super(); + + if (cpus == 0) + cpus = getNumberOfCPUs(); + executor = new ThreadPoolExecutor(cpus, cpus, 5, TimeUnit.SECONDS, new ArrayBlockingQueue(queueSize), + new ThreadPoolExecutor.CallerRunsPolicy()); + } + + @Override + public W3CEndpointReference createMappedResultSet(final W3CEndpointReference source, final UnaryFunction mapper) { + return getResultSetFactory().createResultSet(new ParallelMappedResultSet(source, mapper, getServiceResolver(), executor)); + } + + private int getNumberOfCPUs() { + return Runtime.getRuntime().availableProcessors(); + } + + public int getCpus() { + return cpus; + } + + public void setCpus(int cpus) { + if(cpus > 0) + this.cpus = cpus; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSet.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSet.java new file mode 100644 index 0000000..ef4137f --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSet.java @@ -0,0 +1,71 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.List; + +import eu.dnetlib.enabling.resultset.observer.Observable; + +/** + * A ResultSet represents a set of data results. + * + * @author marko + * + */ +public interface ResultSet extends Observable { + /** + * get the resource identifier. + * + * @return rsId + */ + String getIdentifier(); + + /** + * sets the resource identifier. The ResultSetRegistry will assign a new rsId. + * + * @param rsId allocated resultset identifier + */ + void setIdentifier(String rsId); + + /** + * get a 'page' of results. + * + * @param fromPosition + * from 1 + * @param toPosition + * last included + * @return a page of data + */ + List getResults(int fromPosition, int toPosition); + + /** + * get the number of result elements present in the resultset. + * + * @return number of results + */ + int getNumberOfResults(); + + /** + * Tells if the resultset is open or closed. + * + * @return true if open + */ + boolean isOpen(); + + /** + * close a resultset. + */ + void close(); + + /** + * Tells if the resultset is destroyed. + * + * @return true if the resultset is destroyed. + */ + boolean isDestroyed(); + + /** + * Destroy the resultset and free associated resources, remove it from the ResultSetRegistry. + * After calling this method, the resultset is no more accessible from other services. + */ + void destroy(); + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetAware.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetAware.java new file mode 100644 index 0000000..5fe4137 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetAware.java @@ -0,0 +1,16 @@ +package eu.dnetlib.enabling.resultset; + +/** + * ResultSet listeners can add this interface in order to receive the ResultSet they are bound to. + * + * @author marko + * + */ +public interface ResultSetAware { + /** + * sets the resultset instance. + * + * @param resultSet the resultset instance + */ + void setResultSet(ResultSet resultSet); +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetEPRPool.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetEPRPool.java new file mode 100644 index 0000000..e76b0ea --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetEPRPool.java @@ -0,0 +1,137 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import javax.annotation.PostConstruct; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.tools.UniqueIdentifierGenerator; +import eu.dnetlib.enabling.tools.UniqueIdentifierGeneratorImpl; + +public class ResultSetEPRPool { + private static final Log log = LogFactory.getLog(ResultSetEPRPool.class); // NOPMD by marko on 11/24/08 5:02 PM + + static class PreparedResultSet { + private String rsId; + private W3CEndpointReference epr; + + public PreparedResultSet(final String rsId, final W3CEndpointReference epr) { + super(); + this.rsId = rsId; + this.epr = epr; + } + + public String getRsId() { + return rsId; + } + + public void setRsId(final String rsId) { + this.rsId = rsId; + } + + public W3CEndpointReference getEpr() { + return epr; + } + + public void setEpr(final W3CEndpointReference epr) { + this.epr = epr; + } + } + + class PoolFiller implements Runnable { + + @Override + public void run() { + while (true) + try { + final PreparedResultSet resultSet = prepareResultSet(); + pool.put(resultSet); + } catch (final InterruptedException e) { + // eat this exception + log.debug("cannot add resultset to pool", e); + } + } + + private PreparedResultSet prepareResultSet() { + final String rsId = idGenerator.generateIdentifier(); + return new PreparedResultSet(rsId, createEPR(rsId)); + } + + private W3CEndpointReference createEPR(final String rsId) { + return getResultSetService().getEprBuilder().getEndpointReference(getResultSetService().getEndpoint(), rsId); + } + } + + /** + * resultset service. + */ + private ResultSetServiceImpl resultSetService; + + private int capacity = 1000; + private int fillerThreads = 10; + private final BlockingQueue pool = new LinkedBlockingQueue(capacity); + + /** + * identifier generator. + */ + private UniqueIdentifierGenerator idGenerator = new UniqueIdentifierGeneratorImpl("rs-"); + + @PostConstruct + public void init() { + log.info("-------<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> Starting fill threads"); + for (int i = 0; i < fillerThreads; i++) + startFillter(); + } + + protected void startFillter() { + final Thread filler = new Thread(new PoolFiller()); + filler.setDaemon(true); + filler.start(); + } + + public W3CEndpointReference registerResultSet(final ResultSet resultSet) { + final PreparedResultSet prepared = nextPreparedResultSet(); + if (prepared == null) { + log.info("EPR cache is empty, creating EPR in caller thread"); + return getResultSetService().getEprBuilder().getEndpointReference(getResultSetService().getEndpoint(), idGenerator.generateIdentifier()); + } + + getResultSetService().getResultsetRegistry().addResultSet(resultSet, prepared.getRsId()); + return prepared.getEpr(); + } + + private PreparedResultSet nextPreparedResultSet() { + log.info(">>>> fetching a prepared resultset from the pool"); + return pool.poll(); + } + + public int getCapacity() { + return capacity; + } + + public void setCapacity(int capacity) { + this.capacity = capacity; + } + + public UniqueIdentifierGenerator getIdGenerator() { + return idGenerator; + } + + public void setIdGenerator(UniqueIdentifierGenerator idGenerator) { + this.idGenerator = idGenerator; + } + + public ResultSetServiceImpl getResultSetService() { + return resultSetService; + } + + @Required + public void setResultSetService(ResultSetServiceImpl resultSetService) { + this.resultSetService = resultSetService; + } +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetFactory.java new file mode 100644 index 0000000..244726a --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetFactory.java @@ -0,0 +1,19 @@ +package eu.dnetlib.enabling.resultset; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * Creates a resultset service from a producer. + * + * @author marko + * + */ +public interface ResultSetFactory { + /** + * TODO other parameters like expiration time etc. + * + * @param provider a resultset listener which provides the data + * @return an EPR to the resultset. + */ + W3CEndpointReference createResultSet(ResultSetListener provider); +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetListener.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetListener.java new file mode 100644 index 0000000..3b6df43 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetListener.java @@ -0,0 +1,15 @@ +package eu.dnetlib.enabling.resultset; + + +/** + * A ResultSetListener is a pull interface to a ResultSet. + * + * The Resultset will call method of ResultSetListener implementors whenever it needs to pull some data and provide it + * to clients. The ResultSet service acts as a decouple point between the producer and the consumer. + * + * @author marko + * + */ +public interface ResultSetListener extends TypedResultSetListener { + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetPropertyDao.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetPropertyDao.java new file mode 100644 index 0000000..00d779d --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetPropertyDao.java @@ -0,0 +1,19 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.Map; + +/** + * access resultset properties. + * + * @author marko + * + */ +public interface ResultSetPropertyDao { + /** + * return a map of custom resultset properties. + * + * @param resultSet resultset + * @return property map + */ + Map getProperties(ResultSet resultSet); +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetPropertyDaoImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetPropertyDaoImpl.java new file mode 100644 index 0000000..0bc0b70 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetPropertyDaoImpl.java @@ -0,0 +1,23 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.HashMap; +import java.util.Map; + +/** + * fake resultset property dao. + * + * @author marko + * + */ +public class ResultSetPropertyDaoImpl implements ResultSetPropertyDao { + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.resultset.ResultSetPropertyDao#getProperties(eu.dnetlib.enabling.resultset.ResultSet) + */ + @Override + public Map getProperties(final ResultSet resultSet) { + return new HashMap(); + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetRegistry.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetRegistry.java new file mode 100644 index 0000000..3a420af --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetRegistry.java @@ -0,0 +1,75 @@ +package eu.dnetlib.enabling.resultset; + +import eu.dnetlib.enabling.resultset.observer.ResultSetObserver; + +/** + * Instances of ResultSetRegistry manage a set of resultset objects and manage their garbage collection. + * + * @author marko + * + */ +public interface ResultSetRegistry extends ResultSetObserver { + /** + * add a resultset object to the registry. + * + * The given resultset object from now on is managed by the resultset registry. + * + * @param resultSet + * a resultset object + */ + void addResultSet(ResultSet resultSet); + + /** + * add a resultset object to the registry. + * + * The given resultset object from now on is managed by the resultset registry. + * + * @param resultSet + * a resultset object + * @param identifier the identifier you want for the resultset + */ + void addResultSet(ResultSet resultSet, String identifier); + + /** + * add a resultset object to the registry. + * + * The given resultset object from now on is managed by the resultset registry. + * + * @param resultSet + * a resultset object + * @param maxIdleTime + * max idle time + */ + void addResultSet(ResultSet resultSet, int maxIdleTime); + + /** + * add a resultset object to the registry. + * + * The given resultset object from now on is managed by the resultset registry. + * + * @param resultSet + * a resultset object + * @param identifier the identifier you want for the resultset + * @param maxIdleTime max idle time + */ + void addResultSet(ResultSet resultSet, String identifier, int maxIdleTime); + + /** + * obtain the resultset with the given id. + * + * @param rsId + * resultset id + * @return the resultset object matching the rsId or null + */ + ResultSet getResultSetById(String rsId); + + /** + * obtain the resultset's maxIdleTime parameter for a resultset with the given id. + * + * @param rsId + * resultset id + * @return max idle time in seconds, as specified when the resultset has been registered + */ + int getMaxIdleTimeById(String rsId); + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetRegistryImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetRegistryImpl.java new file mode 100644 index 0000000..df536a5 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetRegistryImpl.java @@ -0,0 +1,123 @@ +package eu.dnetlib.enabling.resultset; + +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.enabling.tools.UniqueIdentifierGenerator; +import eu.dnetlib.enabling.tools.UniqueIdentifierGeneratorImpl; +import eu.dnetlib.miscutils.cache.Cache; + +/** + * Implement a in memory resultset registry. + * + * @author marko + * + */ +public class ResultSetRegistryImpl implements ResultSetRegistry { + + /** + * rsId -> ResultSet mappings are stored here. + */ + private Cache cache; + + /** + * rsID -> initial maxIdleTime time are store here. + */ + private Cache maxIdleTimeCache; + + /** + * identifier generator. + */ + private UniqueIdentifierGenerator idGenerator = new UniqueIdentifierGeneratorImpl("rs-"); + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.resultset.ResultSetRegistry#addResultSet(eu.dnetlib.enabling.resultset.ResultSet) + */ + @Override + public void addResultSet(final ResultSet resultSet) { + addResultSet(resultSet, 0); + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.resultset.ResultSetRegistry#addResultSet(eu.dnetlib.enabling.resultset.ResultSet, java.lang.String) + */ + @Override + public void addResultSet(final ResultSet resultSet, final String identifier) { + addResultSet(resultSet, identifier, 0); + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.resultset.ResultSetRegistry#addResultSet(eu.dnetlib.enabling.resultset.ResultSet, int) + */ + @Override + public void addResultSet(final ResultSet resultSet, final int maxIdleTime) { + addResultSet(resultSet, idGenerator.generateIdentifier(), maxIdleTime); + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.resultset.ResultSetRegistry#addResultSet(eu.dnetlib.enabling.resultset.ResultSet, java.lang.String, int) + */ + @Override + public void addResultSet(final ResultSet resultSet, final String identifier, final int maxIdleTime) { + resultSet.setIdentifier(identifier); + resultSet.addObserver(this); + cache.put(resultSet.getIdentifier(), resultSet); + maxIdleTimeCache.put(resultSet.getIdentifier(), maxIdleTime); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.ResultSetRegistry#getResultSetById(java.lang.String) + */ + @Override + public ResultSet getResultSetById(final String rsId) { + return cache.get(rsId); + } + + @Override + public int getMaxIdleTimeById(final String rsId) { + return maxIdleTimeCache.get(rsId); + } + + + public Cache getCache() { + return cache; + } + + @Required + public void setCache(final Cache cache) { + this.cache = cache; + } + + /** + * {@inheritDoc} + * @see eu.dnetlib.miscutils.observer.Observer#update(eu.dnetlib.enabling.resultset.observer.Observable, java.lang.Object) + */ + @Override + public void update(final ResultSet observed, final Object arg) { + if (!observed.isOpen()) + cache.remove(observed.getIdentifier()); + } + + public UniqueIdentifierGenerator getIdGenerator() { + return idGenerator; + } + + public void setIdGenerator(final UniqueIdentifierGenerator idGenerator) { + this.idGenerator = idGenerator; + } + + public Cache getMaxIdleTimeCache() { + return maxIdleTimeCache; + } + + @Required + public void setMaxIdleTimeCache(Cache maxIdleTimeCache) { + this.maxIdleTimeCache = maxIdleTimeCache; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetServiceImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetServiceImpl.java new file mode 100644 index 0000000..bc1d88b --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/ResultSetServiceImpl.java @@ -0,0 +1,259 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.List; + +import javax.jws.WebService; +import javax.xml.ws.Endpoint; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Required; + +import eu.dnetlib.common.rmi.UnimplementedException; +import eu.dnetlib.enabling.resultset.push.PushResultSet; +import eu.dnetlib.enabling.resultset.push.PushResultSetFactory; +import eu.dnetlib.enabling.resultset.rmi.ResultSetException; +import eu.dnetlib.enabling.resultset.rmi.ResultSetService; +import eu.dnetlib.enabling.tools.AbstractBaseService; +import eu.dnetlib.soap.EndpointReferenceBuilder; + +/** + * This component dispatches the service method calls to stateful datastructure instances and to the singleton core. + * + * @author marko + * + */ +@WebService(targetNamespace = "http://services.dnetlib.eu/") +public class ResultSetServiceImpl extends AbstractBaseService implements ResultSetService { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(ResultSetServiceImpl.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * Maps resultset identifiers to resultsets and manages expiration etc. + */ + private ResultSetRegistry resultsetRegistry; + + /** + * push resultset factory. + */ + private PushResultSetFactory pushFactory; + + /** + * Service endpoint. + */ + private Endpoint endpoint; + + /** + * injected epr builder. + */ + private EndpointReferenceBuilder eprBuilder; + + /** + * custom resultset properties. + */ + private ResultSetPropertyDao customPropertyDao; + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#closeRS(java.lang.String) + */ + @Override + public void closeRS(final String rsId) { + try { + final ResultSet resultSet = getResultSetById(rsId); + resultSet.close(); + } catch (ResultSetException e) { + log.warn("should throw checked exception but wasn't declared in cnr-rmi-api", e); + throw new IllegalStateException(e); + } + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#createPullRS(java.lang.String, java.lang.String, int, int, java.lang.String, + * java.lang.Integer, java.lang.Integer) + */ + @Override + public W3CEndpointReference createPullRS(final String providerAddress, + final String bdId, + final int initialPageSize, + final int expiryTime, + final String styleSheet, + final Integer keepAliveTime, + final Integer total) { + throw new UnimplementedException(); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#createPullRSEPR(javax.xml.ws.wsaddressing.W3CEndpointReference, + * java.lang.String, int, int, java.lang.String, java.lang.Integer, java.lang.Integer) + */ + @Override + public W3CEndpointReference createPullRSEPR(final W3CEndpointReference dataProviderEPR, + final String bdId, + final int initialPageSize, + final int expiryTime, + final String styleSheet, + final Integer keepAliveTime, + final Integer total) { + throw new UnimplementedException(); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#getNumberOfElements(java.lang.String) + */ + @Override + public int getNumberOfElements(final String rsId) throws ResultSetException { + final ResultSet resultSet = getResultSetById(rsId); // NOPMD + return resultSet.getNumberOfResults(); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#getResult(java.lang.String, int, int, java.lang.String) + */ + @Override + public List getResult(final String rsId, final int fromPosition, final int toPosition, final String requestMode) throws ResultSetException { + final ResultSet resultSet = getResultSetById(rsId); // NOPMD + return resultSet.getResults(fromPosition, toPosition); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#createPushRS(int, int) + */ + @Override + public W3CEndpointReference createPushRS(final int expiryTime, final int keepAliveTime) throws ResultSetException { + return pushFactory.createPushResultSet(expiryTime); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#getProperty(java.lang.String, java.lang.String) + */ + @Override + public String getProperty(final String rsId, final String name) throws ResultSetException { + final ResultSet resultSet = getResultSetById(rsId); // NOPMD + + if ("rsId".equals(name)) return resultSet.getIdentifier(); + else if ("total".equals(name)) return Integer.toString(resultSet.getNumberOfResults()); + else if ("maxExpiryTime".equals(name)) return Integer.toString(resultsetRegistry.getMaxIdleTimeById(rsId)); + else if ("expiryTime".equals(name)) return Integer.toString(resultsetRegistry.getMaxIdleTimeById(rsId)); + else if ("keepAliveTime".equals(name)) return Integer.toString(resultsetRegistry.getMaxIdleTimeById(rsId)); + else if ("classSimpleName".equals(name)) return resultSet.getClass().getSimpleName(); + // ... + + return customPropertyDao.getProperties(resultSet).get(name); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#getRSStatus(java.lang.String) + */ + @Override + public String getRSStatus(final String rsId) throws ResultSetException { + final ResultSet resultSet = getResultSetById(rsId); // NOPMD + if (resultSet.isOpen()) return "open"; + return "closed"; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.rmi.ResultSetService#populateRS(java.lang.String, java.util.List) + */ + @Override + public String populateRS(final String rsId, final List elements) throws ResultSetException { + final ResultSet resultSet = getResultSetById(rsId); // NOPMD + if (resultSet instanceof PushResultSet) { + ((PushResultSet) resultSet).addElements(elements); + } else throw new ResultSetException("ResultSet '" + rsId + "' is not a push resultset"); + + return "1"; + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.common.rmi.BaseService#start() + */ + @Override + public void start() { + // TODO Auto-generated method stub + + } + + /** + * obtain the resultset object for a given id. + * + * @param rsId + * resultset id + * @return resultset object, never null + * @throws ResultSetException + * thrown when the id doesn't exist; + */ + private ResultSet getResultSetById(final String rsId) throws ResultSetException { + final ResultSet resultSet = resultsetRegistry.getResultSetById(rsId); // NOPMD + if (resultSet == null) throw new ResultSetException("resultset with id '" + rsId + "' doesn't exist"); + return resultSet; + } + + public ResultSetRegistry getResultsetRegistry() { + return resultsetRegistry; + } + + @Required + public void setResultsetRegistry(final ResultSetRegistry resultsetRegistry) { + this.resultsetRegistry = resultsetRegistry; + } + + public EndpointReferenceBuilder getEprBuilder() { + return eprBuilder; + } + + @Required + public void setEprBuilder(final EndpointReferenceBuilder eprBuilder) { + this.eprBuilder = eprBuilder; + } + + public Endpoint getEndpoint() { + return endpoint; + } + + @Required + public void setEndpoint(final Endpoint endpoint) { + this.endpoint = endpoint; + } + + public PushResultSetFactory getPushFactory() { + return pushFactory; + } + + @Required + public void setPushFactory(final PushResultSetFactory pushFactory) { + this.pushFactory = pushFactory; + } + + public ResultSetPropertyDao getCustomPropertyDao() { + return customPropertyDao; + } + + public void setCustomPropertyDao(final ResultSetPropertyDao customPropertyDao) { + this.customPropertyDao = customPropertyDao; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/SizedIterable.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/SizedIterable.java new file mode 100644 index 0000000..59fe72f --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/SizedIterable.java @@ -0,0 +1,7 @@ +package eu.dnetlib.enabling.resultset; + +public interface SizedIterable extends Iterable { + + public int getNumberOfElements(); + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/StreamingResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/StreamingResultSetFactory.java new file mode 100644 index 0000000..d86f1ef --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/StreamingResultSetFactory.java @@ -0,0 +1,32 @@ +package eu.dnetlib.enabling.resultset; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.springframework.beans.factory.annotation.Required; + +public class StreamingResultSetFactory { + /** + * underlying resultset factory, which exposes local resultsets to the world. + */ + private ResultSetFactory resultSetFactory; + + /** + * @param items + * @param size the size of the iterable + * @return + */ + public W3CEndpointReference createStreamingResultSet(final Iterable items, int size) { + return resultSetFactory.createResultSet(new StreamingResultSetListener(items.iterator(), size)); + } + + + public ResultSetFactory getResultSetFactory() { + return resultSetFactory; + } + + @Required + public void setResultSetFactory(ResultSetFactory resultSetFactory) { + this.resultSetFactory = resultSetFactory; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/StreamingResultSetListener.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/StreamingResultSetListener.java new file mode 100644 index 0000000..1fb9cd3 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/StreamingResultSetListener.java @@ -0,0 +1,132 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Exposes an iterable through our resultset protocol. + * + *

+ * The resultset protocol is constructed as if it were random access. Some kind of sources, like an iterable, is not + * random access. The consumer of such a resultset knows that, and won't try to access the resultset randomly. However, + * it could ask for some data which is already sent, because of retries caused by network issues. + *

+ * + * @author marko + * + */ +public class StreamingResultSetListener implements ResultSetListener, ResultSetAware { + private static final Log log = LogFactory.getLog(StreamingResultSetListener.class); // NOPMD by marko on 11/24/08 5:02 PM + + private ResultSet resultSet; + + private Iterable iterable; + private Iterator items; + private int size; + + private int lastFrom = 0; + private int lastTo = 0; + private List lastChunk; + + public StreamingResultSetListener(Iterable items, int size) { + this(items.iterator(), size); + this.iterable = items; + } + + public StreamingResultSetListener(Iterator items, int size) { + super(); + this.items = items; + this.size = size; + } + + protected void reset() { + lastFrom = 0; + items = iterable.iterator(); + } + + @Override + public List getResult(int from, int to) { + + log.debug("STREAM: getResult(" + from + ", " + to + ")"); + + // handle retry because of network error. + if (from == lastFrom && to == lastTo) + return lastChunk; + + if (from != lastTo + 1) { + if (from == 1 && iterable != null) + reset(); + else + throw new IllegalArgumentException("this resultset is not random access, you can only retry last chunk only, asked from " + from + " to " + + to + " but lastTo = " + lastTo + ". Size = " + size); + } + + List chunk = new ArrayList(); + for (int i = from; i <= to; i++) + chunk.add(items.next()); + + if (resultSet != null && !items.hasNext()) + resultSet.close(); + + lastFrom = from; + lastTo = to; + lastChunk = chunk; + + return chunk; + } + + @Override + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public Iterator getItems() { + return items; + } + + public void setItems(Iterator items) { + this.items = items; + } + + public int getLastFrom() { + return lastFrom; + } + + public void setLastFrom(int lastFrom) { + this.lastFrom = lastFrom; + } + + public int getLastTo() { + return lastTo; + } + + public void setLastTo(int lastTo) { + this.lastTo = lastTo; + } + + public List getLastChunk() { + return lastChunk; + } + + public void setLastChunk(List lastChunk) { + this.lastChunk = lastChunk; + } + + public ResultSet getResultSet() { + return resultSet; + } + + @Override + public void setResultSet(ResultSet resultSet) { + this.resultSet = resultSet; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/TypedResultSetListener.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/TypedResultSetListener.java new file mode 100644 index 0000000..528f0a9 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/TypedResultSetListener.java @@ -0,0 +1,34 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.List; + +/** + * A TypedResultSetListener is a pull interface to a ResultSet. + * + * The Resultset will call method of ResultSetListener implementors whenever it needs to pull some data and provide it + * to clients. The ResultSet service acts as a decouple point between the producer and the consumer. + * + * @author marko + * + */ +public interface TypedResultSetListener { + + /** + * get a single page of results. + * + * @param fromPosition + * from 1 + * @param toPosition + * last included + * @return page of results + */ + List getResult(int fromPosition, int toPosition); + + /** + * get number of elements. + * + * @return number of elements + */ + int getSize(); + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/XSLTMappedResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/XSLTMappedResultSetFactory.java new file mode 100644 index 0000000..16201d4 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/XSLTMappedResultSetFactory.java @@ -0,0 +1,75 @@ +package eu.dnetlib.enabling.resultset; + +import java.util.Map; +import java.util.UUID; + +import javax.xml.transform.Source; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.springframework.core.io.Resource; + +import eu.dnetlib.miscutils.functional.xml.ApplyXslt; + +/** + * Create a mapped resultset using a given XSLT. + * + * @author marko + * + */ +public class XSLTMappedResultSetFactory extends MappedResultSetFactory { + + public W3CEndpointReference createMappedResultSet(final W3CEndpointReference source, final String xslt) { + return createMappedResultSet(source, new ApplyXslt(xslt)); + } + + /** + * Create a mapped resultset using a given XSLT. + * + * @param source + * source + * @param xslt + * XSLT source + * @return epr of mapped resultset + * @throws TransformerConfigurationException + * could happen + */ + public W3CEndpointReference createMappedResultSet(final W3CEndpointReference source, final String xslt, final String name) + throws TransformerConfigurationException { + return createMappedResultSet(source, new ApplyXslt(xslt, name)); + } + + public W3CEndpointReference createMappedResultSet(final W3CEndpointReference source, final Resource xslt) { + return createMappedResultSet(source, new ApplyXslt(xslt)); + } + + public W3CEndpointReference createMappedResultSet(final W3CEndpointReference source, final Source xslt) { + return createMappedResultSet(source, new ApplyXslt(xslt)); + } + + public W3CEndpointReference createMappedResultSet(final W3CEndpointReference source, final Resource xslt, final Map parameters) { + return createMappedResultSet(source, new ApplyXslt(xslt, parameters)); + } + + public W3CEndpointReference createMappedResultSet(final W3CEndpointReference source, final String xslt, final Map parameters) { + return createMappedResultSet(source, new ApplyXslt(xslt, "rs-" + UUID.randomUUID(), parameters)); + } + + /** + * Create a mapped resultset using a given XSLT. + * + * @param source + * source + * @param xslt + * XSLT + * @return epr of mapped resultset + * @throws TransformerFactoryConfigurationError + * @throws TransformerConfigurationException + * could happen + */ + public W3CEndpointReference createMappedResultSet(final W3CEndpointReference source, final Source xslt, final String name) { + return createMappedResultSet(source, new ApplyXslt(xslt, name)); + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/observer/DelegationObserver.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/observer/DelegationObserver.java new file mode 100644 index 0000000..a941307 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/observer/DelegationObserver.java @@ -0,0 +1,29 @@ +package eu.dnetlib.enabling.resultset.observer; + +import eu.dnetlib.enabling.resultset.ResultSet; +import eu.dnetlib.enabling.resultset.ResultSetRegistry; + +/** + * This class delegates an observation event from the java.util.Observable producer to the real consumer which is not a + * java.util.Observer (as expected by java.util.Observable) but instead a miscutils Observer. + * + * @author marko, claudio, alessia, michele + * + */ +public class DelegationObserver implements java.util.Observer { + /** + * + */ + private transient final ResultSet observable; + private transient final ResultSetRegistry observer; + + public DelegationObserver(final ResultSet observable, final ResultSetRegistry observer) { + this.observable = observable; + this.observer = observer; + } + + @Override + public void update(final java.util.Observable ignored, final Object arg) { + observer.update(observable, arg); + } +} \ No newline at end of file diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/observer/Observable.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/observer/Observable.java new file mode 100644 index 0000000..9c20ed5 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/observer/Observable.java @@ -0,0 +1,18 @@ +package eu.dnetlib.enabling.resultset.observer; + +import eu.dnetlib.enabling.resultset.ResultSetRegistry; + +/** + * Declares the JDK-like standard observable pattern as a java interface. + * + * @author marko, claudio, alessia, michele + */ +public interface Observable { + /** + * add a given observer to this object. + * + * @param observer + * observer + */ + void addObserver(ResultSetRegistry observer); +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/observer/ResultSetObserver.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/observer/ResultSetObserver.java new file mode 100644 index 0000000..20c78f4 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/observer/ResultSetObserver.java @@ -0,0 +1,23 @@ +package eu.dnetlib.enabling.resultset.observer; + +import eu.dnetlib.enabling.resultset.ResultSet; + + +/** + * Implement the same interface as JDK java.util.Observer as java interface (not as a class) and introduce static type + * safeness through generics. + * + * @author marko, claudio, alessia, michele + * + */ +public interface ResultSetObserver { + /** + * gets notified when an observed object is updated. + * + * @param observed + * observed object which modification triggered this call. + * @param arg + * arg + */ + void update(ResultSet observed, Object arg); +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/AbstractPushResultSetFactoryImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/AbstractPushResultSetFactoryImpl.java new file mode 100644 index 0000000..a4dfdfd --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/AbstractPushResultSetFactoryImpl.java @@ -0,0 +1,44 @@ +package eu.dnetlib.enabling.resultset.push; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import eu.dnetlib.enabling.resultset.ResultSetServiceImpl; + +/** + * push resultset factory. + * + * @author marko + * + */ +public abstract class AbstractPushResultSetFactoryImpl implements PushResultSetFactory { + /** + * resultset service. + */ + private ResultSetServiceImpl resultSetService; + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.resultset.push.PushResultSetFactory#createPushResultSet(int) + */ + @Override + public W3CEndpointReference createPushResultSet(final int maxIdleTime) { + final PushResultSet resultSet = newInstance(); + resultSetService.getResultsetRegistry().addResultSet(resultSet, maxIdleTime); + return resultSetService.getEprBuilder().getEndpointReference(resultSetService.getEndpoint(), resultSet.getIdentifier()); + } + + /** + * subclasses should override it and create a new push resultset instance. + * @return push resultset instance + */ + protected abstract PushResultSet newInstance(); + + public ResultSetServiceImpl getResultSetService() { + return resultSetService; + } + + public void setResultSetService(final ResultSetServiceImpl resultSetService) { + this.resultSetService = resultSetService; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/CacheTransientResultSetDaoImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/CacheTransientResultSetDaoImpl.java new file mode 100644 index 0000000..c308e38 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/CacheTransientResultSetDaoImpl.java @@ -0,0 +1,179 @@ +package eu.dnetlib.enabling.resultset.push; + +import java.util.ArrayList; +import java.util.List; + +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.resultset.push.ResultSetDescriptor.Range; +import eu.dnetlib.miscutils.cache.Cache; +import eu.dnetlib.miscutils.factory.Factory; + +/** + * implement a resultset dao. + * + * @author marko + * + */ +public class CacheTransientResultSetDaoImpl implements TransientPushResultSetDao { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(CacheTransientResultSetDaoImpl.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * range cache. Keys are "rsid-rangenumber". + */ + private Cache> cache; + + /** + * resultsets + */ + private Cache resultSetCache; + + /** + * resultset descriptor factory. + */ + @Resource + private Factory resultSetDescriptorFactory; + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.push.TransientPushResultSetDao#getSize(java.lang.String) + */ + @Override + public int getSize(final String key) { + final ResultSetDescriptor desc = resultSetCache.get(key); + if (desc == null) + return 0; + return (desc.getRanges() - 1) * desc.getRangeLength() + cache.get(key + "-" + desc.getLastRange()).size(); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.push.TransientPushResultSetDao#addElements(java.lang.String, java.util.List) + */ + @Override + public void addElements(final String key, final List elements) { + // TODO: huuuuu, chaos, fix this please! + synchronized (cache) { + + ResultSetDescriptor desc = resultSetCache.get(key); + + log.debug("got desc: " + desc); + if (desc == null) { + desc = resultSetDescriptorFactory.newInstance(); + resultSetCache.put(key, desc); + } + log.debug("desc now is desc: " + desc); + + if (elements.size() > desc.getRangeLength()) + throw new IllegalArgumentException("The current implementation of the push resultset doesn't accept pages longer than " + + desc.getRangeLength() + ", got: " + elements.size()); + + + int lastRangeIndex = desc.getLastRange(); + + if (lastRangeIndex < 0) + lastRangeIndex = 0; + + log.debug("last range: " + lastRangeIndex); + + List lastRange = cache.get(key + "-" + lastRangeIndex); + log.debug("stored last range: " + lastRange); + + if (lastRange == null) { + lastRange = new ArrayList(); + desc.setRanges(desc.getRanges() + 1); + } + + log.debug("last range is: " + lastRange); + + final int free = desc.getRangeLength() - lastRange.size(); + log.debug("free: " + free); + log.debug("desc range length: " + desc.getRangeLength()); + log.debug("last range size: " + lastRange.size()); + + int toElements = free; + if (toElements > elements.size()) { + toElements = elements.size(); + } else { + + final List nextRange = new ArrayList(elements.subList(free, elements.size())); + log.debug("next range: " + nextRange); + + cache.put(key + "-" + (lastRangeIndex + 1), nextRange); + log.debug("next range stored at: " + key + "-" + (lastRangeIndex + 1)); + desc.setRanges(desc.getRanges() + 1); + } + lastRange.addAll(elements.subList(0, toElements)); + + log.debug("LAST RANGE SIZE: " + lastRange.size() + " range index " + lastRangeIndex); + + log.debug("after add: " + lastRange); + + cache.put(key + "-" + lastRangeIndex, lastRange); + log.debug("range stored at: " + key + "-" + lastRangeIndex); + + resultSetCache.put(key, desc); + } + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.push.TransientPushResultSetDao#getElements(java.lang.String, int, int) + */ + @Override + public List getElements(final String key, final int fromPosition, final int toPosition) { + ResultSetDescriptor desc = resultSetCache.get(key); + log.debug("got desc: " + desc); + + if (desc == null) + desc = resultSetDescriptorFactory.newInstance(); + + final List res = new ArrayList(); + + log.debug("ranges containing " + fromPosition + " to " + toPosition); + for (final Range range : desc.getRangesContaining(fromPosition, toPosition)) { + log.debug("reading range " + key + "-" + range.getRange() + " begin: " + range.getBegin() + " end: " + range.getEnd()); + res.addAll(cache.get(key + "-" + range.getRange()).subList(range.getBegin(), range.getEnd())); + } + + return res; + } + + public Cache> getCache() { + return cache; + } + + @Required + public void setCache(final Cache> cache) { + this.cache = cache; + } + + public Cache getResultSetCache() { + return resultSetCache; + } + + @Required + public void setResultSetCache(final Cache resultSetCache) { + this.resultSetCache = resultSetCache; + } + + public Factory getResultSetDescriptorFactory() { + return resultSetDescriptorFactory; + } + + public void setResultSetDescriptorFactory(final Factory resultSetDescriptorFactory) { + this.resultSetDescriptorFactory = resultSetDescriptorFactory; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/PushResultSet.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/PushResultSet.java new file mode 100644 index 0000000..af76d07 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/PushResultSet.java @@ -0,0 +1,22 @@ +package eu.dnetlib.enabling.resultset.push; + +import java.util.List; + +import eu.dnetlib.enabling.resultset.ResultSet; + +/** + * resultset which receive data through the "push" interface, i.e. data is directly appended to it. + * + * @author marko + * + */ +public interface PushResultSet extends ResultSet { + + /** + * add a list of elements at the end of the resultset. + * + * @param elements list of elements + */ + void addElements(List elements); + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/PushResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/PushResultSetFactory.java new file mode 100644 index 0000000..7597e3d --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/PushResultSetFactory.java @@ -0,0 +1,21 @@ +package eu.dnetlib.enabling.resultset.push; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +/** + * creates a new push resultset. + * + * @author marko + * + */ +public interface PushResultSetFactory { + + /** + * create a new push resultset. + * + * @param maxIdleTime max time the resultset can be idle + * @return push resultset epr + */ + W3CEndpointReference createPushResultSet(int maxIdleTime); + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptor.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptor.java new file mode 100644 index 0000000..8878b9f --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptor.java @@ -0,0 +1,156 @@ +package eu.dnetlib.enabling.resultset.push; + +import java.io.Serializable; +import java.util.Iterator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Describes a push resultset. + * + *

+ * A push resultset stores it's data in several ranges + *

+ * + * @author marko + * + */ +public class ResultSetDescriptor implements Serializable { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(Range.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * This class describes a range. + * + * @author marko + * + */ + public class Range { + private int range; + private int begin; + private int end; + + public Range(final int range, final int begin, final int end) { + super(); + this.range = range; + this.begin = begin; + this.end = end; + } + + @Override + public String toString() { + return "Range(" + range + ", " + begin + ", " + end + ")"; + } + + public int getRange() { + return range; + } + + public void setRange(final int range) { + this.range = range; + } + + public int getBegin() { + return begin; + } + + public void setBegin(final int begin) { + this.begin = begin; + } + + public int getEnd() { + return end; + } + + public void setEnd(final int end) { + this.end = end; + } + } + + /** + * + */ + private static final long serialVersionUID = 7699992350256317181L; + + private int rangeLength = 100; + + private int ranges = 0; + + public int getLastRange() { + return ranges - 1; + } + + /** + * @param fromPosition + * 1 based count + * @param toPosition + * 1 based count + * @return + */ + public Iterable getRangesContaining(final int fromPosition, final int toPosition) { + + final int fromRange = (fromPosition - 1) / rangeLength; + int ttoRange = (int) Math.ceil((toPosition * 1.0) / rangeLength); + + if (ttoRange > ranges) + ttoRange = ranges; + + final int toRange = ttoRange; + + log.debug("FROM range: " + fromRange); + log.debug("TTO range: " + ttoRange); + log.debug("TO range: " + toRange); + + return new Iterable() { + + @Override + public Iterator iterator() { + return new Iterator() { + + int current = fromRange; + + @Override + public boolean hasNext() { + return current < toRange; + } + + @Override + public Range next() { + int begin = 0; + int end = rangeLength; + if (current == fromRange) + begin = (fromPosition - 1) % rangeLength; + if ((current + 1) == toRange) + end = (toPosition - 1) % rangeLength + 1; + return new Range(current++, begin, end); + } + + @Override + public void remove() { + throw new IllegalStateException("text"); + } + }; + } + }; + } + + public int getRangeLength() { + return rangeLength; + } + + public void setRangeLength(final int rangeLength) { + this.rangeLength = rangeLength; + } + + public int getRanges() { + return ranges; + } + + public void setRanges(final int ranges) { + this.ranges = ranges; + } +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorFactory.java new file mode 100644 index 0000000..8c39800 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorFactory.java @@ -0,0 +1,7 @@ +package eu.dnetlib.enabling.resultset.push; + +import eu.dnetlib.miscutils.factory.Factory; + +public interface ResultSetDescriptorFactory extends Factory { + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetDao.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetDao.java new file mode 100644 index 0000000..f5276f6 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetDao.java @@ -0,0 +1,45 @@ +package eu.dnetlib.enabling.resultset.push; + +import java.util.List; + +/** + * offer access to the underlying in-memory or pseudo-memory (serialization backed caches) storage for the transient + * push-resultsets. + * + * This type of push resultsets doesn't even try to make efficient access to records, they just keep them as big lists + * on the underlying storage. Actual implementations may decide to keep them on the heap or to use some caching + * technology like ehcache. + * + * @author marko + * + */ +public interface TransientPushResultSetDao { + /** + * add elements to a given key. + * + * @param key + * usually a rsid. + * @param elements + * list of elements + */ + void addElements(String key, List elements); + + /** + * obtain content for a given key (resultset) in a given range (1 based). + * + * @param key + * usually a rsid + * @param fromPosition from position (1 based, inclusive) + * @param toPosition to position (1 based, inclusive) + * @return all the elements for a given key + */ + List getElements(String key, int fromPosition, int toPosition); + + /** + * get the resultset size (number of elements). + * + * @param key usually a rsid + * @return resultset size + */ + int getSize(String key); +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetFactory.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetFactory.java new file mode 100644 index 0000000..33a7b95 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetFactory.java @@ -0,0 +1,34 @@ +package eu.dnetlib.enabling.resultset.push; + + +/** + * creates a transient push resultset. + * + * @author marko + * + */ +public class TransientPushResultSetFactory extends AbstractPushResultSetFactoryImpl { + + /** + * dao. + */ + private TransientPushResultSetDao dao; + + /** + * {@inheritDoc} + * @see eu.dnetlib.enabling.resultset.push.AbstractPushResultSetFactoryImpl#newInstance() + */ + @Override + protected PushResultSet newInstance() { + return new TransientPushResultSetImpl(dao); + } + + public TransientPushResultSetDao getDao() { + return dao; + } + + public void setDao(final TransientPushResultSetDao dao) { + this.dao = dao; + } + +} diff --git a/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetImpl.java b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetImpl.java new file mode 100644 index 0000000..2eb4ac1 --- /dev/null +++ b/dnet-core-services/src/main/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetImpl.java @@ -0,0 +1,99 @@ +package eu.dnetlib.enabling.resultset.push; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import eu.dnetlib.enabling.resultset.AbstractObservableResultset; + +/** + * a push resultset which holds it's data in a transient cache. + * + * @author marko + * + */ +public class TransientPushResultSetImpl extends AbstractObservableResultset implements PushResultSet { + + /** + * logger. + */ + private static final Log log = LogFactory.getLog(TransientPushResultSetImpl.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * dao. + */ + private final transient TransientPushResultSetDao dao; + + /** + * rsId. + */ + private String identifier; + + /** + * constructed by the transient push resultset factory. + * + * @param dao dao + */ + public TransientPushResultSetImpl(final TransientPushResultSetDao dao) { + super(); + this.dao = dao; + setOpen(true); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.push.PushResultSet#addElements(java.util.List) + */ + @Override + public void addElements(final List elements) { + log.debug("adding to push RS: " + elements); + + if (isOpen()) + dao.addElements(getIdentifier(), elements); + else + throw new IllegalStateException("cannot write to a closed push resultset"); + } + + @Override + public int getNumberOfResults() { + return dao.getSize(getIdentifier()); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.ResultSet#getResults(int, int) + */ + @Override + public List getResults(final int fromPosition, final int toPosition) { + + final int size = getNumberOfResults(); + int toPos = toPosition; + int fromPos = fromPosition; + if (fromPos > size) + fromPos = size; + if (toPos > size) + toPos = size; + + log.debug("calling get elements: " + getIdentifier() + " from " + fromPos + " to " + toPos); + return dao.getElements(getIdentifier(), fromPos, toPos); + } + + public TransientPushResultSetDao getDao() { + return dao; + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public void setIdentifier(final String identifier) { + this.identifier = identifier; + } + + +} diff --git a/dnet-core-services/src/main/resources/eu/dnetlib/enabling/hcm/applicationContext-hcm.xml b/dnet-core-services/src/main/resources/eu/dnetlib/enabling/hcm/applicationContext-hcm.xml new file mode 100644 index 0000000..60e92b3 --- /dev/null +++ b/dnet-core-services/src/main/resources/eu/dnetlib/enabling/hcm/applicationContext-hcm.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-services/src/main/resources/eu/dnetlib/enabling/hnm/applicationContext-hnm.xml b/dnet-core-services/src/main/resources/eu/dnetlib/enabling/hnm/applicationContext-hnm.xml new file mode 100644 index 0000000..eaef353 --- /dev/null +++ b/dnet-core-services/src/main/resources/eu/dnetlib/enabling/hnm/applicationContext-hnm.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-services/src/main/resources/eu/dnetlib/enabling/resultset/applicationContext-resultset.properties b/dnet-core-services/src/main/resources/eu/dnetlib/enabling/resultset/applicationContext-resultset.properties new file mode 100644 index 0000000..27391e1 --- /dev/null +++ b/dnet-core-services/src/main/resources/eu/dnetlib/enabling/resultset/applicationContext-resultset.properties @@ -0,0 +1,10 @@ +services.is.resultset.resultSetRegistryCache.timeToIdle = 240 +services.is.resultset.resultSetRegistryMaxIdleTimeCache.timeToIdle = 240 + +# jaxwsEndpointReferenceBuilder | staticResultSetServiceEprBuilder +services.resultset.eprbuilder.bean = jaxwsEndpointReferenceBuilder +services.resultset.endpoint.static.address = ${transport.soap.baseAddress}/resultSet + +services.is.resultset.push.timeToIdle=36000 +services.is.resultset.push.maxElementsInMemory=500 +services.is.resultset.push.maxElementsOnDisk=5000000 diff --git a/dnet-core-services/src/main/resources/eu/dnetlib/enabling/resultset/applicationContext-resultset.xml b/dnet-core-services/src/main/resources/eu/dnetlib/enabling/resultset/applicationContext-resultset.xml new file mode 100644 index 0000000..36a66ae --- /dev/null +++ b/dnet-core-services/src/main/resources/eu/dnetlib/enabling/resultset/applicationContext-resultset.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/FetchListTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/FetchListTest.java new file mode 100644 index 0000000..5c61b36 --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/FetchListTest.java @@ -0,0 +1,75 @@ +package eu.dnetlib.enabling.resultset; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class FetchListTest { + + /** + * object to test + */ + private FetchList fetchList; + + private List list; + private Iterator iter; + + private int FETCH_SIZE = 10; + + private int TOTAL_ELEMENTS = 100; + + @Before + public void setUp() throws Exception { + list = new ArrayList(); + for (int i = 0; i < TOTAL_ELEMENTS; i++) { + list.add("XXX" + i); + } + this.iter = list.iterator(); + fetchList = new FetchList(iter, FETCH_SIZE); + + } + + @Test + public void testFetchList() { + assertNotNull(fetchList); + } + + @Test + public void testPoll_consumedElements() { + assertEquals(0, fetchList.getConsumedElements()); + fetchList.poll(); + assertEquals(1, fetchList.getConsumedElements()); + } + + @Test + public void testFill_totalElements() { + assertEquals(FETCH_SIZE, fetchList.getTotalElements()); + } + + @Test + public void testPoll() { + for (int i = 0; i < TOTAL_ELEMENTS; i++) { + assertEquals(i, fetchList.getConsumedElements()); + assertEquals(list.get(i), fetchList.poll()); + } + assertEquals(TOTAL_ELEMENTS, fetchList.getConsumedElements()); + assertEquals(TOTAL_ELEMENTS, fetchList.getTotalElements()); + } + + @Test + public void testPoll_null() { + for (int i = 0; i < TOTAL_ELEMENTS; i++) { + fetchList.poll(); + } + assertNull(fetchList.poll()); + assertNull(fetchList.poll()); + assertNull(fetchList.poll()); + } +} diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/IterableResultSetTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/IterableResultSetTest.java new file mode 100644 index 0000000..bdb9c76 --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/IterableResultSetTest.java @@ -0,0 +1,150 @@ +package eu.dnetlib.enabling.resultset; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Iterator; +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; + +/** + * tests the IterableResult class + * + * @author claudio + * + */ +@RunWith(MockitoJUnitRunner.class) +public class IterableResultSetTest { + + /** + * number of total elements + */ + private static int NUM_ELEM = 100; + + /** + * number of elements available to fetch + */ + private static int FETCH_SIZE = 10; + + @Mock + private ResultSet mockResultSet; + + /** + * a list used to compare elements. + */ + private List list; + + /** + * Iterator associated with the test list. + */ + private Iterator iter; + + /** + * Object to test. + */ + private IterableResultSet iterResultSet; + + @Before + public void setUp() { + list = new ArrayList(); + for (int i=0; i< NUM_ELEM; i++) + list.add("XXXXX " + i); + + iter = list.iterator(); + when(mockResultSet.isOpen()).thenReturn(iter.hasNext()); + + iterResultSet = new IterableResultSet(list, FETCH_SIZE); + iterResultSet.setResultSet(mockResultSet); + } + + @Test + public void testIterableResultSet() { + assertNotNull(iterResultSet); + assertTrue(iterResultSet.getSize() > 0); + } + + @Test(expected=RuntimeException.class) + public void testGetResult_fail_1() { + iterResultSet.getResult(0, 5); + } + + @Test(expected=RuntimeException.class) + public void testGetResult_fail_2() { + iterResultSet.getResult(1, 5); + iterResultSet.getResult(1, 5); + } + + @Test(expected=RuntimeException.class) + public void testGetResult_fail_3() { + iterResultSet.getResult(1, 5); + iterResultSet.getResult(6, 5); + } + + @Test(expected=RuntimeException.class) + public void testGetResult_fail_4() { + iterResultSet.getResult(1, 5); + iterResultSet.getResult(7, 10); + } + + @Test + public void testGetResult_success_1() { + List result = iterResultSet.getResult(1, NUM_ELEM); + for (int i=0; i result = iterResultSet.getResult(1, NUM_ELEM); + + for (int i=0; i getResult(final int fromPosition, final int toPosition) { + final List res = new ArrayList(); + for (int i = fromPosition; i <= toPosition; i++) + res.add(((Integer) (i - 1)).toString()); + return res; + } + + @Override + public int getSize() { + return RESULT_SIZE; + } + } + + /** + * Mockito lacks of multiple interfaces mocking. + * + * @author marko + * + */ + private abstract static class AbstractResultSetAware implements ResultSetListener, ResultSetAware { + } + + /** + * resultset registry mock. + */ + @Mock + private transient ResultSetRegistry registry; + + /** + * object under test. + */ + private transient LocalResultSetImpl resultSet; + + /** + * number of returned test results. + */ + private static final int RESULT_SIZE = 3; + + /** + * provides data to the resultset under test. + */ + private transient TestResultSetListener resultSetListener; + + /** + * prepare. + * + */ + @Before + public void setUp() { + resultSetListener = new TestResultSetListener(); + resultSet = new LocalResultSetImpl(resultSetListener); + } + + /** + * test the get number of results method. + */ + @Test + public void testGetNumberOfResults() { + assertEquals("check number of results", RESULT_SIZE, resultSet.getNumberOfResults()); + } + + /** + * test the get result method. + */ + @Test + public void testGetResults() { + final List res = resultSet.getResults(1, RESULT_SIZE); // NOPMD by marko on 11/27/08 3:00 AM + assertEquals("whole length", RESULT_SIZE, res.size()); + for (int i = 0; i < RESULT_SIZE; i++) + assertEquals("check element", ((Integer) i).toString(), res.get(i)); + + final List res2 = resultSet.getResults(1, RESULT_SIZE - 1); // NOPMD by marko on 11/27/08 3:00 AM + assertEquals("shorter", RESULT_SIZE - 1, res2.size()); + + final List res3 = resultSet.getResults(1, RESULT_SIZE + 1); // NOPMD by marko on 11/27/08 3:00 AM + assertEquals("to out of bounds", RESULT_SIZE, res3.size()); + + final List res4 = resultSet.getResults(RESULT_SIZE + 1, RESULT_SIZE); // NOPMD by marko on 11/27/08 3:00 AM + assertEquals("from out of bounds", 0, res4.size()); + + final List res5 = resultSet.getResults(0, RESULT_SIZE); // NOPMD by marko on 11/27/08 3:00 AM + assertEquals("from lesser than 1", RESULT_SIZE, res5.size()); + + final List res6 = resultSet.getResults(RESULT_SIZE, 1); // NOPMD by marko on 11/27/08 3:00 AM + assertEquals("inverted from and to", 1, res6.size()); + + resultSet.setOpen(true); + final List res7 = resultSet.getResults(1, RESULT_SIZE + 1); // NOPMD by marko on 11/27/08 3:00 AM + assertEquals("to out of bounds - pass through", RESULT_SIZE + 1, res7.size()); + } + + /** + * test observer/observable pattern. + */ + @Test + public void testDestroy() { + resultSet.addObserver(registry); + assertEquals("observer should be added", 1, resultSet.countObservers()); + + resultSet.destroy(); + assertTrue("should be destroyed", resultSet.isDestroyed()); + + assertEquals("observers should be cleared", 0, resultSet.countObservers()); + verify(registry, times(1)).update(resultSet, null); + } + + /** + * test getter ... let's make code coverage happy. + */ + @Test + public void testGetListener() { + assertEquals("test getter?", resultSetListener, resultSet.getListener()); + } + + /** + * test setter ... let's make code coverage happy. + */ + @Test + public void testSetListener() { + assertEquals("test getter?", resultSetListener, resultSet.getListener()); + resultSet.setListener(null); + assertNull("test setter?", resultSet.getListener()); + } + + /** + * test. + */ + @Test + public void testResultSetAware() { + final AbstractResultSetAware abr = mock(AbstractResultSetAware.class); + resultSet = new LocalResultSetImpl(abr); + assertNotNull("dummy", resultSet); + + verify(abr).setResultSet((ResultSet) anyObject()); + + } + +} diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/MappedResultSetTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/MappedResultSetTest.java new file mode 100644 index 0000000..958d8ea --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/MappedResultSetTest.java @@ -0,0 +1,140 @@ +package eu.dnetlib.enabling.resultset; // NOPMD + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; + +import java.util.List; + +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import com.google.common.collect.Lists; + +import eu.dnetlib.enabling.resultset.rmi.ResultSetException; +import eu.dnetlib.enabling.resultset.rmi.ResultSetService; +import eu.dnetlib.enabling.tools.ServiceResolver; +import eu.dnetlib.miscutils.factory.Factory; +import eu.dnetlib.miscutils.functional.ThreadSafeUnaryFunction; +import eu.dnetlib.miscutils.functional.UnaryFunction; + +/** + * test xslt resultset. + * + * @author marko + * + */ +@RunWith(MockitoJUnitRunner.class) +public class MappedResultSetTest { + + /** + * instance under test. + */ + private transient MappedResultSet mappedResultSet; + + /** + * service resolver. + */ + @Mock + private transient ServiceResolver serviceResolver; + + /** + * resultset service mock. + */ + @Mock + private transient ResultSetService resultSetService; + + /** + * resultset mock. + */ + @Mock + private transient ResultSet resultSet; + + private Answer resultSetAnswer = new Answer() { + + private List list = Lists.newArrayList("1","2","3","4","5","6","7","8","9","10"); + + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + String method = invocation.getMethod().getName(); + if (method.equals("getResult") || method.equals("getResults")) { + int from = (Integer) invocation.getArguments()[1]; + int to = (Integer) invocation.getArguments()[2]; + + return list.subList(from-1, to); + } + if (method.equals("getNumberOfElements") || method.equals("getNumberOfResults")) { + return list.size(); + } + if (method.equals("getRSStatus")) { + return "closed"; + } + if (method.equals("isOpen")) { + return false; + } + System.out.println(invocation.toString()); + return null; + } + }; + + /** + * Common setup. + * @throws ResultSetException + * + */ + @Before + public void setUp() throws ResultSetException { + + when(serviceResolver.getService(eq(ResultSetService.class), any(W3CEndpointReference.class))).thenReturn(resultSetService); + when(serviceResolver.getResourceIdentifier(any(W3CEndpointReference.class))).thenReturn("123"); + + when(resultSetService.getNumberOfElements(anyString())).thenAnswer(resultSetAnswer); + when(resultSetService.getResult(anyString(), anyInt(), anyInt(), anyString())).thenAnswer(resultSetAnswer ); + when(resultSetService.getRSStatus(anyString())).thenAnswer(resultSetAnswer); + + when(resultSet.getNumberOfResults()).thenAnswer(resultSetAnswer); + when(resultSet.getResults(anyInt(), anyInt())).thenAnswer(resultSetAnswer ); + when(resultSet.isOpen()).thenAnswer(resultSetAnswer); + } + + @Test + @Ignore + public void testThreadSafeMappedResultSet_1() { + + final Factory> functionFactory = new Factory>() { + @Override + public UnaryFunction newInstance() { + return new UnaryFunction() { + @Override + public String evaluate(String arg) { + return "mapped-" + arg; + } + }; + } + }; + mappedResultSet = new MappedResultSet(null, new ThreadSafeUnaryFunction(functionFactory), serviceResolver); + mappedResultSet.setResultSet(resultSet); + + for (String s : mappedResultSet.getResult(1, 10)) { + assertNotNull("null result", s); + assertFalse("empty result", s.isEmpty()); + assertNotNull("null element", s); + assertTrue("transformed correctly", s.startsWith("mapped-")); + } + } +} + diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/ResultSetRegistryImplTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/ResultSetRegistryImplTest.java new file mode 100644 index 0000000..4768eb2 --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/ResultSetRegistryImplTest.java @@ -0,0 +1,184 @@ +package eu.dnetlib.enabling.resultset; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.matches; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; + +import org.junit.After; +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.miscutils.cache.EhCache; + +/** + * Test ResultSetRegistryImpl. + * + * @author marko + * + */ +@RunWith(MockitoJUnitRunner.class) +public class ResultSetRegistryImplTest { + + /** + * max in memory elements. + */ + private static final int MAX_IN_MEM = 100; + + /** + * default time. + */ + private static final int DEFAULT_TTI = 1000; + + /** + * default time to live. + */ + private static final int DEFAULT_TTL = 1000; + + /** + * class under test. + */ + private transient ResultSetRegistryImpl registry; + + /** + * mock of a resultset object. + */ + @Mock + private transient ResultSet resultSet; + + /** + * ehcache manager. + */ + private final transient CacheManager cacheManager = CacheManager.create(); + + /** + * Set up basic mocks. + */ + @Before + public void setUp() { + final Cache ehCache = new net.sf.ehcache.Cache("testCache", MAX_IN_MEM, false, false, DEFAULT_TTL, DEFAULT_TTI); + cacheManager.addCache(ehCache); + + final Cache mtEhCache = new net.sf.ehcache.Cache("testMTCache", MAX_IN_MEM, false, false, DEFAULT_TTL, DEFAULT_TTI); + cacheManager.addCache(mtEhCache); + + final EhCache cache = new EhCache(ehCache); + cache.setCache(ehCache); + + final EhCache mtCache = new EhCache(mtEhCache); + mtCache.setCache(mtEhCache); + + registry = new ResultSetRegistryImpl(); + registry.setCache(cache); + registry.setMaxIdleTimeCache(mtCache); + + when(resultSet.getIdentifier()).thenReturn("123456"); + when(resultSet.isOpen()).thenReturn(true); + } + + /** + * remove ehcache from ehcachemanager. + */ + @After + public void tearDown() { + cacheManager.removeAllCaches(); + } + + /** + * Test that registered resultsets are really registered. + */ + @Test + public void testAddResultSet() { + + final String rsId = resultSet.getIdentifier(); + assertNotNull("the resultset should not be null", rsId); + + registry.addResultSet(resultSet); + verify(resultSet).setIdentifier(matches("rs-" + registry.getIdGenerator().getRegEx())); + + final ResultSet res = registry.getResultSetById(rsId); // NOPMD + assertNotNull("the resultset is not found", res); + assertEquals("the resultsets don't have the same id", rsId, res.getIdentifier()); + assertEquals("the resultsets are not the same instance", resultSet, res); + } + + /** + * Test that registered resultsets are really registered. + */ + @Test + public void testAddResultSetWithId() { + resultSet = mock(ResultSet.class); + when(resultSet.getIdentifier()).thenReturn("123"); + + final String rsId = resultSet.getIdentifier(); + + registry.addResultSet(resultSet, rsId); + verify(resultSet).setIdentifier(rsId); + + final ResultSet res = registry.getResultSetById(rsId); // NOPMD + assertNotNull("the resultset is not found", res); + assertEquals("the resultsets don't have the same id", rsId, res.getIdentifier()); + assertEquals("the resultsets are not the same instance", resultSet, res); + } + + /** + * try to obtain a inexistent resultset. + */ + @Test + public void testInexistentResultSet() { + final String rsId = resultSet.getIdentifier(); + assertNull("inexisten resultset returns null", registry.getResultSetById(rsId)); + } + + /** + * test closing of resultset and its pruning from the registry. + */ + @Test + public void testResultSetClose() { + final String rsId = resultSet.getIdentifier(); + + assertTrue("check if resultset is open", resultSet.isOpen()); + + registry.addResultSet(resultSet); + verify(resultSet).addObserver(registry); + + // simulate a resultset close on the mock + when(resultSet.isOpen()).thenReturn(false); + registry.update(resultSet, null); + + assertFalse("check if resultset is closed", resultSet.isOpen()); + assertNull("check if the object is pruned from the registry", registry.getResultSetById(rsId)); + } + + /** + * same as testResultSetClose() but with a real observer/observable mechanism, i.e. without resultset mock class. + * + */ + @Test + public void testWithRealObservable() { + final LocalResultSetImpl rset = new LocalResultSetImpl(null); + rset.setIdentifier("123456"); + resultSet = rset; + + final String rsId = resultSet.getIdentifier(); + + assertFalse("check if resultset is not destroyed", resultSet.isDestroyed()); + + registry.addResultSet(resultSet); + resultSet.destroy(); + + assertTrue("check if resultset is destroyed", resultSet.isDestroyed()); + assertNull("check if the object is pruned from the registry", registry.getResultSetById(rsId)); + } + +} diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/ResultSetServiceImplTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/ResultSetServiceImplTest.java new file mode 100644 index 0000000..7d509a2 --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/ResultSetServiceImplTest.java @@ -0,0 +1,101 @@ +package eu.dnetlib.enabling.resultset; + +import static org.junit.Assert.*; // NOPMD +import static org.mockito.Mockito.*; // NOPMD + +import java.util.HashMap; +import java.util.Map; + +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.resultset.rmi.ResultSetException; + +/** + * resultset service rmi implementation test. + * + * @author marko + * + */ +@RunWith(MockitoJUnitRunner.class) +public class ResultSetServiceImplTest { + + /** + * mock resultset id. + */ + private static final String RS_ID = "123"; + + /** + * instance under test. + */ + private transient ResultSetServiceImpl service; + + /** + * custom property dao mock. + */ + @Mock + private transient ResultSetPropertyDao customPropertyDao; + + /** + * resultset registry mock. + */ + @Mock + private transient ResultSetRegistry resultsetRegistry; + + /** + * resultset instance mock. + */ + @Mock + private transient ResultSet resultSet; + + /** + * setup. + */ + @Before + public void setUp() { + service = new ResultSetServiceImpl(); + service.setCustomPropertyDao(customPropertyDao); + service.setResultsetRegistry(resultsetRegistry); + + when(resultsetRegistry.getResultSetById(RS_ID)).thenReturn(resultSet); + when(resultSet.getIdentifier()).thenReturn(RS_ID); + } + + /** + * test get property. + * @throws ResultSetException shouldn't happen + */ + @Test + public void testGetProperty() throws ResultSetException { + final Map properties = new HashMap(); + properties.put("prefetch", "true"); + + when(customPropertyDao.getProperties(resultSet)).thenReturn(properties); + when(resultSet.getNumberOfResults()).thenReturn(1); + + assertEquals("test builtin", "1", service.getProperty(RS_ID, "total")); + assertEquals("test builtin", RS_ID, service.getProperty(RS_ID, "rsId")); + + assertEquals("test custom", "true", service.getProperty(RS_ID, "prefetch")); + assertNull("test custom", service.getProperty(RS_ID, "unexistent")); + } + + /** + * get resultset status. + * + * @throws ResultSetException shouldn't happen + */ + @Test + public void testGetRSStatus() throws ResultSetException { + when(resultSet.isOpen()).thenReturn(true); + + assertEquals("check open", service.getRSStatus(RS_ID), "open"); + + when(resultSet.isOpen()).thenReturn(false); + assertEquals("check closed", service.getRSStatus(RS_ID), "closed"); + } + +} diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/StreamingResultSetListenerTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/StreamingResultSetListenerTest.java new file mode 100644 index 0000000..7c5d645 --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/StreamingResultSetListenerTest.java @@ -0,0 +1,62 @@ +package eu.dnetlib.enabling.resultset; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.Lists; + +public class StreamingResultSetListenerTest { + + private StreamingResultSetListener listener; + private List data; + + @Before + public void setUp() throws Exception { + data = Lists.newArrayList("uno", "due", "tre", "quattro"); + listener = new StreamingResultSetListener(data.iterator(), data.size()); + } + + @Test + public void testGetResult() { + assertEquals(data.size(), listener.getSize()); + + final List res = listener.getResult(1, 2); + assertEquals(2, res.size()); + assertEquals("uno", res.get(0)); + assertEquals("due", res.get(1)); + } + + @Test + public void testGetResult2() { + listener.getResult(1, 2); + final List res = listener.getResult(3, 4); + assertEquals(2, res.size()); + assertEquals("tre", res.get(0)); + assertEquals("quattro", res.get(1)); + } + + @Test + public void testGetResult_retry() { + listener.getResult(1, 2); + final List res = listener.getResult(1, 2); + assertEquals(2, res.size()); + assertEquals("uno", res.get(0)); + assertEquals("due", res.get(1)); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetResult_random() { + listener.getResult(1, 2); + listener.getResult(2, 3); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetResult_skip() { + listener.getResult(1, 2); + listener.getResult(4, 4); + } +} diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/XSLTMappedResultSetFactoryTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/XSLTMappedResultSetFactoryTest.java new file mode 100644 index 0000000..11a6663 --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/XSLTMappedResultSetFactoryTest.java @@ -0,0 +1,139 @@ +package eu.dnetlib.enabling.resultset; // NOPMD + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.List; + +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import com.google.common.collect.Lists; + +import eu.dnetlib.enabling.resultset.rmi.ResultSetException; +import eu.dnetlib.enabling.resultset.rmi.ResultSetService; +import eu.dnetlib.enabling.tools.ServiceResolver; + +/** + * test xslt resultset. + * + * @author marko + * + */ +@RunWith(MockitoJUnitRunner.class) +public class XSLTMappedResultSetFactoryTest { + + /** + * instance under test. + */ + private transient XSLTMappedResultSetFactory factory; + + /** + * resultset factory mock. + */ + @Mock + private transient ResultSetFactory resultSetFactory; + + /** + * service resolver. + */ + @Mock + private transient ServiceResolver serviceResolver; + + /** + * resultset service mock. + */ + @Mock + private transient ResultSetService resultSetService; + + /** + * underlying resultSet mock + */ + @Mock + private transient ResultSet resultSet; + + /** + * Common setup. + * + */ + @Before + public void setUp() { + factory = new XSLTMappedResultSetFactory(); + factory.setResultSetFactory(resultSetFactory); + factory.setServiceResolver(serviceResolver); + } + + /** + * test invalid xslt. + * + * @throws TransformerConfigurationException + * could happen + */ + @Test(expected = IllegalStateException.class) + public void testInvalidXslt() throws TransformerConfigurationException { + factory.createMappedResultSet(null, ""); + } + + /** + * test xslt. + * + * @throws TransformerConfigurationException + * could happen + * @throws IOException + * could happen + * @throws ResultSetException mock + */ + @Test + public void testXslt() throws TransformerConfigurationException, IOException, ResultSetException { + when(serviceResolver.getService(eq(ResultSetService.class), (W3CEndpointReference) anyObject())).thenReturn(resultSetService); + when(serviceResolver.getResourceIdentifier((W3CEndpointReference) anyObject())).thenReturn("123"); + + //when(resultSetService.getNumberOfElements(anyString())).thenReturn(1); + when(resultSetService.getResult("123", 1, 1, "waiting")).thenReturn(Lists.newArrayList("something")); + when(resultSetService.getRSStatus("123")).thenReturn("closed"); + + when(resultSet.isOpen()).thenReturn(true); + + final StringWriter xsltSource = new StringWriter(); + IOUtils.copy(getClass().getResourceAsStream("test.xsl"), xsltSource); + factory.createMappedResultSet(null, xsltSource.toString()); + + verify(resultSetFactory, times(1)).createResultSet(argThat(new ArgumentMatcher() { + + @Override + public boolean matches(final ResultSetListener argument) { + final MappedResultSet listener = (MappedResultSet) argument; + listener.setResultSet(resultSet); + final List res = listener.getResult(1, 1); + + assertNotNull("null result", res); + assertFalse("empty result", res.isEmpty()); + + assertNotNull("null element", res.get(0)); + + assertTrue("transformed correctly", res.get(0).contains("something")); + + return true; + } + })); + } + +} diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/AbstractTransientPushResultSetDaoTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/AbstractTransientPushResultSetDaoTest.java new file mode 100644 index 0000000..ad4f3fb --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/AbstractTransientPushResultSetDaoTest.java @@ -0,0 +1,223 @@ +package eu.dnetlib.enabling.resultset.push; + +import static org.junit.Assert.*; // NOPMD + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import com.google.common.collect.Lists; + +import eu.dnetlib.miscutils.cache.Cache; + +/** + * subclass this unit test class and provide a concrete transient push resultset dao implementation. + * + * @author marko + * + */ +@Ignore +public abstract class AbstractTransientPushResultSetDaoTest { + /** + * logger. + */ + private static final Log log = LogFactory.getLog(AbstractTransientPushResultSetDaoTest.class); // NOPMD by marko on 11/24/08 5:02 PM + + /** + * test first element. + */ + private static final String TWO = "two"; + + /** + * test second element. + */ + private static final String ONE = "one"; + + /** + * some key. + */ + private static final String RS_ID = "123"; + + /** + * instance under test. + */ + private TransientPushResultSetDao dao; + + /** + * prepare the instance under test. + * + */ + @Before + public void setUp() { + setDao(newInstance()); + } + + /** + * specific unit tests will override this method and provide a concrete instance to test. + * + * @return new transient push resultset dao instance + */ + protected abstract TransientPushResultSetDao newInstance(); + + /** + * test add elements. + */ + @Test + public void testAddElements() { + final List freshGet = getDao().getElements(RS_ID, 1, 0); + assertNotNull("fresh get", freshGet); + assertEquals("fresh get", 0, freshGet.size()); + + final List list = new ArrayList(); + list.add(ONE); + + getDao().addElements(RS_ID, list); + + final List firstGet = getDao().getElements(RS_ID, 1, 1); + assertEquals("first get", 1, firstGet.size()); + assertEquals("first get", ONE, firstGet.get(0)); + + final List listAppend = new ArrayList(); + listAppend.add(TWO); + + getDao().addElements(RS_ID, listAppend); + + final List secondGet = getDao().getElements(RS_ID, 1, 2); + assertEquals("second get", 2, secondGet.size()); + assertEquals("second get", ONE, secondGet.get(0)); + assertEquals("second get", TWO, secondGet.get(1)); + + final List listBig = new ArrayList(); + for (int i = 0; i < 90; i++) + listBig.add(Integer.toString(i)); + + getDao().addElements(RS_ID, listBig); + + final List thirdGet = getDao().getElements(RS_ID, 3, 90); + assertEquals("third get", 88, thirdGet.size()); + assertEquals("third get", "0", thirdGet.get(0)); + assertEquals("third get", "1", thirdGet.get(1)); + } + + /** + * check get elements. + */ + @Test + public void testGetElements() { + final List list = new ArrayList(); + list.add(ONE); + + getDao().addElements(RS_ID, list); + + final List res = getDao().getElements(RS_ID, 1, 1); + assertEquals("check size", 1, res.size()); + assertEquals("check element", ONE, res.get(0)); + } + + /** + * introduced for reproducing of #904. Thanks to Jochen for this one. + */ + @Test(expected = IllegalArgumentException.class) + public void testAddElementsDifferentSize() { + List sizes = Lists.newArrayList(100, 200, 300, 99); + int run = 0; + int total = 0; + + List records = new LinkedList(); + for (int size : sizes) { + for (int i = 0; i < size; i++) + records.add("bla-" + run + "-" + i); + + dao.addElements(RS_ID, records); + records.clear(); + + total += size; + run++; + } + + assertEquals("check size", total, dao.getSize(RS_ID)); + } + + /** + * introduced for reproducing of #904. with correct size (limited by 100) + */ + @Test + public void testAddElementsDifferentSizeCorrectSize() { + List sizes = Lists.newArrayList(51, 51, 51, 51); + + int total = 0; + int run = 0; + String lastName = null; + + List records = new LinkedList(); + for (int size : sizes) { + for (int i = 0; i < size; i++) { + lastName = "bla-" + run + "-" + i; + records.add(lastName); + } + + dao.addElements(RS_ID, records); + records.clear(); + + total += size; + run++; + } + + inspect(); + + assertEquals("check size", total, dao.getSize(RS_ID)); + assertEquals("check last", lastName, dao.getElements(RS_ID, total, total).get(0)); + + assertEquals("check first page", "bla-0-11", dao.getElements(RS_ID, 12, 12).get(0)); + assertEquals("check last page", "bla-1-11", dao.getElements(RS_ID, 51 + 12, 51 + 12).get(0)); + + assertEquals("check range", 10, dao.getElements(RS_ID, 1, 10).size()); + assertEquals("check range", 10, dao.getElements(RS_ID, 51 + 1, 51 + 10).size()); + assertEquals("check range", 51, dao.getElements(RS_ID, 1, 51).size()); + assertEquals("check range", 52, dao.getElements(RS_ID, 1, 52).size()); + assertEquals("check range", 51, dao.getElements(RS_ID, 2, 52).size()); + + assertEquals("check range", 100, dao.getElements(RS_ID, 1, 100).size()); + assertEquals("check range", 99, dao.getElements(RS_ID, 2, 100).size()); + assertEquals("check range", 100, dao.getElements(RS_ID, 2, 101).size()); + + assertEquals("check range", 200, dao.getElements(RS_ID, 2, 201).size()); + } + + private void inspect() { + CacheTransientResultSetDaoImpl dao = (CacheTransientResultSetDaoImpl) this.dao; + ResultSetDescriptor desc = dao.getResultSetCache().get(RS_ID); + Cache> cache = dao.getCache(); + + log.debug("----------------"); + log.debug("ranges: " + desc.getRanges()); + log.debug("last range index: " + desc.getLastRange()); + for (int i = 0; i < desc.getRanges(); i++) { + final List range = cache.get(RS_ID + "-" + i); + log.debug("range: " + i + " has " + range.size() + " elements"); + log.debug(" " + range); + } + + log.debug("----------------"); + } + + @Test + public void testSizeOfEmpty() { + assertEquals("empty size", 0, dao.getSize(RS_ID)); + } + + public TransientPushResultSetDao getDao() { + return dao; + } + + public void setDao(final TransientPushResultSetDao dao) { + this.dao = dao; + } + +} diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/CacheTransientPushResultSetDaoTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/CacheTransientPushResultSetDaoTest.java new file mode 100644 index 0000000..c441b34 --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/CacheTransientPushResultSetDaoTest.java @@ -0,0 +1,78 @@ +package eu.dnetlib.enabling.resultset.push; + +import java.util.List; + +import net.sf.ehcache.CacheManager; + +import org.junit.After; + +import eu.dnetlib.miscutils.cache.Cache; +import eu.dnetlib.miscutils.cache.EhCache; +import eu.dnetlib.miscutils.factory.Factory; + +/** + * concrete push resultset dao test. + * + * @author marko + * + */ +public class CacheTransientPushResultSetDaoTest extends AbstractTransientPushResultSetDaoTest { // NOPMD + + /** + * max in memory elements. + */ + private static final int MAX_IN_MEM = 100; + + /** + * default time. + */ + private static final int DEFAULT_TTI = 1000; + + /** + * default time to live. + */ + private static final int DEFAULT_TTL = 1000; + + /** + * cache manager. + */ + private final transient CacheManager cacheManager = CacheManager.create(); + + /** + * clean cache after test. + */ + @After + public void cleanCache() { + cacheManager.removeAllCaches(); + } + + /** + * {@inheritDoc} + * + * @see eu.dnetlib.enabling.resultset.push.AbstractTransientPushResultSetDaoTest#newInstance() + */ + @Override + protected TransientPushResultSetDao newInstance() { + final net.sf.ehcache.Cache ehcache = new net.sf.ehcache.Cache("testCache", MAX_IN_MEM, false, false, DEFAULT_TTL, DEFAULT_TTI); + cacheManager.addCache(ehcache); + + final net.sf.ehcache.Cache rsehcache = new net.sf.ehcache.Cache("rsetCache", MAX_IN_MEM, false, false, DEFAULT_TTL, DEFAULT_TTI); + cacheManager.addCache(rsehcache); + + final CacheTransientResultSetDaoImpl resultSet = new CacheTransientResultSetDaoImpl(); + final Cache> cache = new EhCache>(ehcache); + final Cache rscache = new EhCache(rsehcache); + resultSet.setCache(cache); + resultSet.setResultSetCache(rscache); + + resultSet.setResultSetDescriptorFactory(new Factory() { + + @Override + public ResultSetDescriptor newInstance() { + return new ResultSetDescriptor(); + } + }); + return resultSet; + } + +} diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorFactoryTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorFactoryTest.java new file mode 100644 index 0000000..da20e12 --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorFactoryTest.java @@ -0,0 +1,36 @@ +package eu.dnetlib.enabling.resultset.push; + + +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; + +import eu.dnetlib.miscutils.factory.Factory; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class ResultSetDescriptorFactoryTest { + + @Resource + Factory factory; + + @Before + public void setUp() throws Exception { + } + + @Test + public void testFactory() { + ResultSetDescriptor desc1 = factory.newInstance(); + ResultSetDescriptor desc2 = factory.newInstance(); + + assertTrue("check different instances", desc1 != desc2); + assertEquals("check that it's a spring prototype", 200, desc1.getRangeLength()); + assertEquals("check that it's a spring prototype", 200, desc2.getRangeLength()); + } +} diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorTest.java new file mode 100644 index 0000000..96c5ac0 --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorTest.java @@ -0,0 +1,44 @@ +package eu.dnetlib.enabling.resultset.push; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Before; +import org.junit.Test; + +import eu.dnetlib.enabling.resultset.push.ResultSetDescriptor.Range; + +public class ResultSetDescriptorTest { + + private static final Log log = LogFactory.getLog(ResultSetDescriptorTest.class); // NOPMD by marko on 11/24/08 5:02 PM + + private transient ResultSetDescriptor descriptor; + + @Before + public void setUp() throws Exception { + descriptor = new ResultSetDescriptor(); + } + + @Test + public void testGetRangesContaining() { + log.debug("--- 1, 1"); + for(Range i : descriptor.getRangesContaining(1, 1)) + log.debug("i " + i); + + log.debug("--- 2, 1000"); + for(Range i : descriptor.getRangesContaining(2, 1000)) + log.debug("i " + i); + + log.debug("--- 5, 2000"); + for(Range i : descriptor.getRangesContaining(5, 2000)) + log.debug("i " + i); + + log.debug("--- 5, 2001"); + for(Range i : descriptor.getRangesContaining(5, 2001)) + log.debug("i " + i); + } + + @Test + public void testNext() { + } + +} diff --git a/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetImplTest.java b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetImplTest.java new file mode 100644 index 0000000..6122974 --- /dev/null +++ b/dnet-core-services/src/test/java/eu/dnetlib/enabling/resultset/push/TransientPushResultSetImplTest.java @@ -0,0 +1,166 @@ +package eu.dnetlib.enabling.resultset.push; + +import static org.junit.Assert.*; // NOPMD +import static org.mockito.Mockito.*; // NOPMD + +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 com.google.common.collect.Lists; + +import eu.dnetlib.enabling.resultset.ResultSetRegistry; + +/** + * test the transient push resultset. + * + * @author marko + * + */ +@RunWith(MockitoJUnitRunner.class) +public class TransientPushResultSetImplTest { + + /** + * first test value. + */ + private static final String ONE = "one"; + + /** + * second test value. + */ + private static final String TWO = "two"; + + /** + * test rs id. + */ + private static final String RS_ID = "123"; + + /** + * instance to be tested. + */ + private transient TransientPushResultSetImpl resultSet; + + /** + * dao mock. + */ + @Mock + private transient TransientPushResultSetDao dao; + + /** + * resultset registry mock. + */ + @Mock + private transient ResultSetRegistry registry; + + /** + * setup class to be tested. + * + * @throws Exception + */ + @Before + public void setUp() { + resultSet = new TransientPushResultSetImpl(dao); + resultSet.setIdentifier(RS_ID); + resultSet.getDao(); // getter code coverage hack + } + + /** + * test adding elements. + */ + @Test + public void testAddElements() { + final List list = new ArrayList(); + list.add(ONE); + + resultSet.addElements(list); + + verify(dao).addElements(RS_ID, list); + assertNotNull("dummy", resultSet); + } + + /** + * test get number of results. + */ + @Test + public void testGetNumberOfResults() { + when(dao.getSize(RS_ID)).thenReturn(1); + + assertEquals("check size", 1, resultSet.getNumberOfResults()); + } + + /** + * get result. + */ + @Test + public void testGetResults() { + final List list = new ArrayList(); + list.add(TWO); + + when(dao.getSize(RS_ID)).thenReturn(2); + when(dao.getElements(RS_ID, 2, 2)).thenReturn(list); + + assertEquals("check list", TWO, resultSet.getResults(2, 2).get(0)); + assertEquals("check size", 1, resultSet.getResults(2, 2).size()); + } + + /** + * test when indices are out of range, trailing stuff should be skipped. + */ + @Test + public void testGetResultOutOfRange() { + final List list = new ArrayList(); + list.add(TWO); + + when(dao.getSize(RS_ID)).thenReturn(2); + when(dao.getElements(RS_ID, 2, 2)).thenReturn(list); + + assertEquals("check list", TWO, resultSet.getResults(2, 2 + 1).get(0)); + assertEquals("check size", 1, resultSet.getResults(2, 2 + 1).size()); + } + + /** + * open. + */ + @Test + public void testIsOpen() { + assertTrue("check is open by default", resultSet.isOpen()); + } + + /** + * test close. + */ + @Test + public void testClose() { + resultSet.addObserver(registry); + assertEquals("observer should be added", 1, resultSet.countObservers()); + + resultSet.destroy(); + assertTrue("should be destroyed", resultSet.isDestroyed()); + + assertEquals("observers should be cleared", 0, resultSet.countObservers()); + verify(registry, times(1)).update(resultSet, null); + } + + @Test + public void testFromAfterSize() { + final List list = new ArrayList(); + list.add(TWO); + + when(dao.getSize(RS_ID)).thenReturn(2); + when(dao.getElements(RS_ID, 2, 2)).thenReturn(list); + + resultSet.getResults(3, 2); + } + + @Test(expected = IllegalStateException.class) + public void testWriteClosed() { + resultSet.close(); + resultSet.addElements(Lists.newArrayList("test")); + } + +} diff --git a/dnet-core-services/src/test/resources/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorFactoryTest-context.xml b/dnet-core-services/src/test/resources/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorFactoryTest-context.xml new file mode 100644 index 0000000..1d9da31 --- /dev/null +++ b/dnet-core-services/src/test/resources/eu/dnetlib/enabling/resultset/push/ResultSetDescriptorFactoryTest-context.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/dnet-core-services/src/test/resources/eu/dnetlib/enabling/resultset/test.xsl b/dnet-core-services/src/test/resources/eu/dnetlib/enabling/resultset/test.xsl new file mode 100644 index 0000000..d9465a0 --- /dev/null +++ b/dnet-core-services/src/test/resources/eu/dnetlib/enabling/resultset/test.xsl @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index c58e1cb..eca7b60 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,7 @@ dnet-core-components + dnet-core-services @@ -141,13 +142,6 @@ gson ${google.gson.version} - - junit - junit - 4.9 - test - - log4j @@ -230,6 +224,12 @@ 1.7 + + junit + junit + 4.9 + test + org.mockito mockito-core @@ -336,7 +336,16 @@ - + + junit + junit + test + + + org.mockito + mockito-core + test +