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