diff --git a/config/alfresco/dao/dao-context.xml b/config/alfresco/dao/dao-context.xml
index 95721aa27f..fc7132cfa0 100644
--- a/config/alfresco/dao/dao-context.xml
+++ b/config/alfresco/dao/dao-context.xml
@@ -253,4 +253,9 @@
+
+
+
+
+
diff --git a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml
index 219a61fddd..a6381530c3 100644
--- a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml
+++ b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml
@@ -41,7 +41,8 @@
-
+
+
diff --git a/config/alfresco/ibatis/ibatis-context.xml b/config/alfresco/ibatis/ibatis-context.xml
index 86d97dd573..964d4765eb 100644
--- a/config/alfresco/ibatis/ibatis-context.xml
+++ b/config/alfresco/ibatis/ibatis-context.xml
@@ -78,5 +78,7 @@
-
+
+
+
diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/solr-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/solr-common-SqlMap.xml
new file mode 100644
index 0000000000..ceb5c0143b
--- /dev/null
+++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/solr-common-SqlMap.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties
index 57385f4f17..4af5a9f6a4 100644
--- a/config/alfresco/version.properties
+++ b/config/alfresco/version.properties
@@ -4,10 +4,10 @@
# Version label
-version.major=3
-version.minor=5
+version.major=4
+version.minor=0
version.revision=0
-version.label=a
+version.label=
# Edition label
@@ -19,4 +19,4 @@ version.build=@build-number@
# Schema number
-version.schema=4306
+version.schema=5000
diff --git a/source/java/org/alfresco/repo/domain/solr/NodeParameters.java b/source/java/org/alfresco/repo/domain/solr/NodeParameters.java
new file mode 100644
index 0000000000..bf720f680e
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/solr/NodeParameters.java
@@ -0,0 +1,167 @@
+package org.alfresco.repo.domain.solr;
+
+import java.util.List;
+import java.util.Set;
+
+import org.alfresco.service.namespace.QName;
+
+/**
+ * Stores node parameters for use in SOLR DAO queries
+ *
+ * @since 4.0
+ */
+public class NodeParameters
+{
+ private List transactionIds;
+ private Long fromNodeId;
+ private Long toNodeId;
+
+ private String storeProtocol;
+ private String storeIdentifier;
+
+ private Set includeNodeTypes;
+ private Set excludeNodeTypes;
+ private List includeTypeIds;
+ private List excludeTypeIds;
+
+ private Set includeAspects;
+ private Set excludeAspects;
+ private List includeAspectIds;
+ private List excludeAspectIds;
+
+ public boolean getStoreFilter()
+ {
+ return (storeProtocol != null || storeIdentifier != null);
+ }
+
+ public void setStoreProtocol(String storeProtocol)
+ {
+ this.storeProtocol = storeProtocol;
+ }
+
+ public String getStoreProtocol()
+ {
+ return storeProtocol;
+ }
+
+ public void setStoreIdentifier(String storeIdentifier)
+ {
+ this.storeIdentifier = storeIdentifier;
+ }
+
+ public String getStoreIdentifier()
+ {
+ return storeIdentifier;
+ }
+
+ public void setTransactionIds(List txnIds)
+ {
+ this.transactionIds = txnIds;
+ }
+
+ public List getTransactionIds()
+ {
+ return transactionIds;
+ }
+
+ public Long getFromNodeId()
+ {
+ return fromNodeId;
+ }
+
+ public void setFromNodeId(Long fromNodeId)
+ {
+ this.fromNodeId = fromNodeId;
+ }
+
+ public Long getToNodeId()
+ {
+ return toNodeId;
+ }
+
+ public void setToNodeId(Long toNodeId)
+ {
+ this.toNodeId = toNodeId;
+ }
+
+ public Set getIncludeNodeTypes()
+ {
+ return includeNodeTypes;
+ }
+
+ public Set getExcludeNodeTypes()
+ {
+ return excludeNodeTypes;
+ }
+
+ public Set getIncludeAspects()
+ {
+ return includeAspects;
+ }
+
+ public Set getExcludeAspects()
+ {
+ return excludeAspects;
+ }
+
+ public void setIncludeNodeTypes(Set includeNodeTypes)
+ {
+ this.includeNodeTypes = includeNodeTypes;
+ }
+
+ public void setExcludeNodeTypes(Set excludeNodeTypes)
+ {
+ this.excludeNodeTypes = excludeNodeTypes;
+ }
+
+ public void setIncludeAspects(Set includeAspects)
+ {
+ this.includeAspects = includeAspects;
+ }
+
+ public void setExcludeAspects(Set excludeAspects)
+ {
+ this.excludeAspects = excludeAspects;
+ }
+
+ public List getIncludeAspectIds()
+ {
+ return includeAspectIds;
+ }
+
+ public void setIncludeAspectIds(List includeAspectIds)
+ {
+ this.includeAspectIds = includeAspectIds;
+ }
+
+ public List getExcludeAspectIds()
+ {
+ return excludeAspectIds;
+ }
+
+ public void setExcludeAspectIds(List excludeAspectIds)
+ {
+ this.excludeAspectIds = excludeAspectIds;
+ }
+
+ public List getIncludeTypeIds()
+ {
+ return includeTypeIds;
+ }
+
+ public void setIncludeTypeIds(List includeTypeIds)
+ {
+ this.includeTypeIds = includeTypeIds;
+ }
+
+ public List getExcludeTypeIds()
+ {
+ return excludeTypeIds;
+ }
+
+ public void setExcludeTypeIds(List excludeTypeIds)
+ {
+ this.excludeTypeIds = excludeTypeIds;
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/domain/solr/SOLRDAO.java b/source/java/org/alfresco/repo/domain/solr/SOLRDAO.java
new file mode 100644
index 0000000000..bfb2363f77
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/solr/SOLRDAO.java
@@ -0,0 +1,31 @@
+package org.alfresco.repo.domain.solr;
+
+import java.util.List;
+
+import org.alfresco.repo.domain.node.Node;
+
+/**
+ * DAO support for SOLR web scripts.
+ *
+ * @since 4.0
+ */
+// TODO - permit shortened form of QNames for e.g. aspects i.e. cm:content vs {http://www.alfresco.org/model/content/1.0}content?
+public interface SOLRDAO
+{
+ public List getTransactions(Long minTxnId, Long fromCommitTime, int maxResults);
+ public void getNodes(NodeParameters nodeParameters, int maxResults, NodeQueryCallback callback);
+
+ /**
+ * The interface that will be used to give query results to the calling code.
+ */
+ public static interface NodeQueryCallback
+ {
+ /**
+ * Handle a node.
+ *
+ * @param node the node
+ * @return Return true to continue processing rows or false to stop
+ */
+ boolean handleNode(Node node);
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/solr/SOLRDAOTest.java b/source/java/org/alfresco/repo/domain/solr/SOLRDAOTest.java
new file mode 100644
index 0000000000..0e621a4b4e
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/solr/SOLRDAOTest.java
@@ -0,0 +1,250 @@
+package org.alfresco.repo.domain.solr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.domain.node.Node;
+import org.alfresco.repo.domain.solr.SOLRDAO.NodeQueryCallback;
+import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.ApplicationContextHelper;
+import org.alfresco.util.PropertyMap;
+import org.springframework.context.ConfigurableApplicationContext;
+
+public class SOLRDAOTest extends TestCase
+{
+ private ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) ApplicationContextHelper.getApplicationContext();
+
+ private AuthenticationComponent authenticationComponent;
+ private TransactionService transactionService;
+ private RetryingTransactionHelper txnHelper;
+ private NodeService nodeService;
+ private FileFolderService fileFolderService;
+ private SOLRDAO solrDAO;
+
+ private StoreRef storeRef;
+ private NodeRef rootNodeRef;
+ private NodeRef container1;
+ private NodeRef content1;
+ private NodeRef content2;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
+ transactionService = serviceRegistry.getTransactionService();
+ txnHelper = transactionService.getRetryingTransactionHelper();
+
+ solrDAO = (SOLRDAO)ctx.getBean("solrDAO");
+ nodeService = (NodeService)ctx.getBean("NodeService");
+ fileFolderService = (FileFolderService)ctx.getBean("FileFolderService");
+ authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
+
+ authenticationComponent.setSystemUserAsCurrentUser();
+
+ storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, getName() + System.currentTimeMillis());
+ rootNodeRef = nodeService.getRootNode(storeRef);
+ }
+
+ private void buildTransactions1()
+ {
+ txnHelper.doInTransaction(new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ PropertyMap props = new PropertyMap();
+ props.put(ContentModel.PROP_NAME, "Container1");
+ container1 = nodeService.createNode(
+ rootNodeRef,
+ ContentModel.ASSOC_CHILDREN,
+ ContentModel.ASSOC_CHILDREN,
+ ContentModel.TYPE_FOLDER,
+ props).getChildRef();
+
+ System.out.println("container1 = " + container1);
+
+ FileInfo content1Info = fileFolderService.create(container1, "Content1", ContentModel.TYPE_CONTENT);
+ content1 = content1Info.getNodeRef();
+
+ System.out.println("content1 = " + content1);
+
+ return null;
+ }
+ });
+
+ txnHelper.doInTransaction(new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ FileInfo content2Info = fileFolderService.create(container1, "Content2", ContentModel.TYPE_CONTENT);
+ content2 = content2Info.getNodeRef();
+
+ System.out.println("content2 = " + content2);
+
+ fileFolderService.delete(content1);
+
+ return null;
+ }
+ });
+ }
+
+ private void buildTransactions2()
+ {
+ txnHelper.doInTransaction(new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ PropertyMap props = new PropertyMap();
+ props.put(ContentModel.PROP_NAME, "Container1");
+ container1 = nodeService.createNode(
+ rootNodeRef,
+ ContentModel.ASSOC_CHILDREN,
+ ContentModel.ASSOC_CHILDREN,
+ ContentModel.TYPE_FOLDER,
+ props).getChildRef();
+
+ System.out.println("container1 = " + container1);
+
+ FileInfo content1Info = fileFolderService.create(container1, "Content1", ContentModel.TYPE_CONTENT);
+ content1 = content1Info.getNodeRef();
+
+ System.out.println("content1 = " + content1);
+
+ return null;
+ }
+ });
+
+ txnHelper.doInTransaction(new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ FileInfo content2Info = fileFolderService.create(container1, "Content2", ContentModel.TYPE_CONTENT);
+ content2 = content2Info.getNodeRef();
+
+ System.out.println("content2 = " + content2);
+
+ fileFolderService.delete(content1);
+
+ return null;
+ }
+ });
+ }
+
+ public void testQueryTransactions1()
+ {
+ long startTime = System.currentTimeMillis();
+
+ buildTransactions1();
+
+ List txns = solrDAO.getTransactions(null, startTime, 0);
+ assertEquals("Number of transactions is incorrect", 2, txns.size());
+
+ int[] updates = new int[] {1, 1};
+ int[] deletes = new int[] {0, 1};
+ List txnIds = new ArrayList(txns.size());
+ int i = 0;
+ for(Transaction txn : txns)
+ {
+ assertEquals("Number of deletes is incorrect", deletes[i], txn.getDeletes());
+ assertEquals("Number of updates is incorrect", updates[i], txn.getUpdates());
+ i++;
+
+ txnIds.add(txn.getId());
+ }
+
+ TestNodeQueryCallback nodeQueryCallback = new TestNodeQueryCallback(container1, content1, content2);
+ NodeParameters nodeParameters = new NodeParameters();
+ nodeParameters.setTransactionIds(txnIds);
+ solrDAO.getNodes(nodeParameters, 0, nodeQueryCallback);
+
+ assertEquals("Unxpected nodes", 3, nodeQueryCallback.getSuccessCount());
+ }
+
+ public void testQueryTransactions2()
+ {
+ long startTime = System.currentTimeMillis();
+
+ buildTransactions2();
+
+ List txns = solrDAO.getTransactions(null, startTime, 0);
+ assertEquals("Number of transactions is incorrect", 2, txns.size());
+
+ int[] updates = new int[] {1, 1};
+ int[] deletes = new int[] {0, 1};
+ List txnIds = new ArrayList(txns.size());
+ int i = 0;
+ for(Transaction txn : txns)
+ {
+ assertEquals("Number of deletes is incorrect", deletes[i], txn.getDeletes());
+ assertEquals("Number of updates is incorrect", updates[i], txn.getUpdates());
+ i++;
+
+ txnIds.add(txn.getId());
+ }
+
+ TestNodeQueryCallback nodeQueryCallback = new TestNodeQueryCallback(container1, content1, content2);
+ NodeParameters nodeParameters = new NodeParameters();
+ nodeParameters.setTransactionIds(txnIds);
+ solrDAO.getNodes(nodeParameters, 0, nodeQueryCallback);
+
+ assertEquals("Unxpected nodes", 3, nodeQueryCallback.getSuccessCount());
+ }
+
+
+ private static class TestNodeQueryCallback implements NodeQueryCallback
+ {
+ private int successCount = 0;
+ private NodeRef container1;
+ private NodeRef content1;
+ private NodeRef content2;
+
+ public TestNodeQueryCallback(NodeRef container1,
+ NodeRef content1, NodeRef content2) {
+ super();
+ this.container1 = container1;
+ this.content1 = content1;
+ this.content2 = content2;
+ }
+
+ @Override
+ public boolean handleNode(Node node) {
+ NodeRef nodeRef = node.getNodeRef();
+ Boolean isDeleted = node.getDeleted();
+
+ System.out.println("Node: " + node.toString());
+
+ if(nodeRef.equals(container1) && !isDeleted)
+ {
+ successCount++;
+ }
+
+ if(nodeRef.equals(content1) && isDeleted)
+ {
+ successCount++;
+ }
+
+ if(nodeRef.equals(content2) && !isDeleted)
+ {
+ successCount++;
+ }
+ return true;
+ }
+
+ public int getSuccessCount()
+ {
+ return successCount;
+ }
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/domain/solr/SOLRTransaction.java b/source/java/org/alfresco/repo/domain/solr/SOLRTransaction.java
new file mode 100644
index 0000000000..ac6f5f76a5
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/solr/SOLRTransaction.java
@@ -0,0 +1,14 @@
+package org.alfresco.repo.domain.solr;
+
+/**
+ * Interface for SOLR transaction objects.
+ *
+ * @since 4.0
+ */
+public interface SOLRTransaction
+{
+ public Long getId();
+ public Long getCommitTimeMs();
+ public int getUpdates();
+ public int getDeletes();
+}
diff --git a/source/java/org/alfresco/repo/domain/solr/SOLRTransactionParameters.java b/source/java/org/alfresco/repo/domain/solr/SOLRTransactionParameters.java
new file mode 100644
index 0000000000..4ef21fe183
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/solr/SOLRTransactionParameters.java
@@ -0,0 +1,76 @@
+package org.alfresco.repo.domain.solr;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Holds parameters for SOLR DAO calls
+ *
+ * @since 4.0
+ */
+public class SOLRTransactionParameters {
+ private Long minTxnId;
+ private Long txnFromCommitTime;
+ private List transactionIds;
+ private Long fromNodeId;
+ private Long toNodeId;
+
+ public SOLRTransactionParameters()
+ {
+ }
+
+ public void setMinTxnId(Long minTxnId)
+ {
+ this.minTxnId = minTxnId;
+ }
+
+ public Long getMinTxnId()
+ {
+ return minTxnId;
+ }
+
+ public void setTxnFromCommitTime(Long txnFromCommitTime) {
+ this.txnFromCommitTime = txnFromCommitTime;
+ }
+
+ public Long getTxnFromCommitTime() {
+ return txnFromCommitTime;
+ }
+
+ public void setTransactionIds(List txnIds) {
+ this.transactionIds = txnIds;
+ }
+
+ public List getTransactionIds() {
+ return transactionIds;
+ }
+
+ public Long getFromNodeId() {
+ return fromNodeId;
+ }
+
+ public void setFromNodeId(Long fromNodeId) {
+ this.fromNodeId = fromNodeId;
+ }
+
+ public Long getToNodeId() {
+ return toNodeId;
+ }
+
+ public void setToNodeId(Long toNodeId) {
+ this.toNodeId = toNodeId;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(512);
+ sb.append("SOLRTransactionParameters")
+ .append(", txnFromCommitTime").append(txnFromCommitTime == null ? null : new Date(txnFromCommitTime))
+ .append(", fromNodeId").append(fromNodeId == null ? null : fromNodeId)
+ .append(", toNodeId").append(toNodeId == null ? null : toNodeId)
+ .append(", txnIds").append(transactionIds == null ? null : transactionIds.size())
+ .append("]");
+ return sb.toString();
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/solr/Transaction.java b/source/java/org/alfresco/repo/domain/solr/Transaction.java
new file mode 100644
index 0000000000..2b118fd1bc
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/solr/Transaction.java
@@ -0,0 +1,14 @@
+package org.alfresco.repo.domain.solr;
+
+/**
+ * Interface for SOLR transaction objects.
+ *
+ * @since 4.0
+ */
+public interface Transaction
+{
+ public Long getId();
+ public Long getCommitTimeMs();
+ public int getUpdates();
+ public int getDeletes();
+}
diff --git a/source/java/org/alfresco/repo/domain/solr/TransactionEntity.java b/source/java/org/alfresco/repo/domain/solr/TransactionEntity.java
new file mode 100644
index 0000000000..92a4d80faa
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/solr/TransactionEntity.java
@@ -0,0 +1,75 @@
+package org.alfresco.repo.domain.solr;
+
+/**
+ * Bean to represent SOLR transaction data.
+ *
+ * @since 4.0
+ */
+public class TransactionEntity implements Transaction
+{
+ private Long id;
+ private Long commitTimeMs;
+ private int updates;
+ private int deletes;
+
+ /**
+ * Required default constructor
+ */
+ public TransactionEntity()
+ {
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(512);
+ sb.append("TransactionEntity")
+ .append("[ ID=").append(id)
+ .append(", updates=").append(updates)
+ .append(", deletes=").append(deletes)
+ .append(", commitTimeMs=").append(commitTimeMs)
+ .append("]");
+ return sb.toString();
+ }
+
+ public Long getId()
+ {
+ return id;
+ }
+
+ public void setId(Long id)
+ {
+ this.id = id;
+ }
+
+ public int getUpdates()
+ {
+ return updates;
+ }
+
+ public void setUpdates(int updates)
+ {
+ this.updates = updates;
+ }
+
+ public int getDeletes()
+ {
+ return deletes;
+ }
+
+ public void setDeletes(int deletes)
+ {
+ this.deletes = deletes;
+ }
+
+ public Long getCommitTimeMs()
+ {
+ return commitTimeMs;
+ }
+
+ public void setCommitTimeMs(Long commitTimeMs)
+ {
+ this.commitTimeMs = commitTimeMs;
+ }
+}
+
diff --git a/source/java/org/alfresco/repo/domain/solr/ibatis/SOLRDAOImpl.java b/source/java/org/alfresco/repo/domain/solr/ibatis/SOLRDAOImpl.java
new file mode 100644
index 0000000000..9b0de872a5
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/solr/ibatis/SOLRDAOImpl.java
@@ -0,0 +1,147 @@
+package org.alfresco.repo.domain.solr.ibatis;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.alfresco.repo.domain.node.Node;
+import org.alfresco.repo.domain.node.NodeEntity;
+import org.alfresco.repo.domain.qname.QNameDAO;
+import org.alfresco.repo.domain.solr.NodeParameters;
+import org.alfresco.repo.domain.solr.SOLRDAO;
+import org.alfresco.repo.domain.solr.SOLRTransactionParameters;
+import org.alfresco.repo.domain.solr.Transaction;
+import org.springframework.orm.ibatis.SqlMapClientTemplate;
+
+/**
+ * DAO support for SOLR web scripts.
+ *
+ * @since 4.0
+ */
+public class SOLRDAOImpl implements SOLRDAO
+{
+ private static final String SELECT_TRANSACTIONS = "alfresco.solr.select_Txns";
+ private static final String SELECT_NODES = "alfresco.solr.select_Txn_Nodes";
+
+ private QNameDAO qnameDAO;
+ private SqlMapClientTemplate template;
+
+ public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate)
+ {
+ this.template = sqlMapClientTemplate;
+ }
+
+ public SqlMapClientTemplate getSqlMapClientTemplate()
+ {
+ return this.template;
+ }
+
+ public void setQNameDAO(QNameDAO qnameDAO)
+ {
+ this.qnameDAO = qnameDAO;
+ }
+
+ /*
+ * Initialize
+ */
+ public void init()
+ {
+ }
+
+ @SuppressWarnings("unchecked")
+ public List getTransactions(Long minTxnId, Long fromCommitTime, int maxResults)
+ {
+ if(minTxnId == null && fromCommitTime == null && (maxResults == 0 || maxResults == Integer.MAX_VALUE))
+ {
+ throw new IllegalArgumentException("Must specify at least one parameter");
+ }
+
+ List txns = null;
+ SOLRTransactionParameters params = new SOLRTransactionParameters();
+ params.setMinTxnId(minTxnId);
+ params.setTxnFromCommitTime(fromCommitTime);
+
+ if(maxResults != 0 && maxResults != Integer.MAX_VALUE)
+ {
+ txns = (List)template.queryForList(SELECT_TRANSACTIONS, params, 0, maxResults);
+ }
+ else
+ {
+ txns = (List)template.queryForList(SELECT_TRANSACTIONS, params);
+ }
+
+ return txns;
+ }
+
+ @SuppressWarnings("unchecked")
+ // TODO should create qnames if don't exist?
+ public void getNodes(NodeParameters nodeParameters, int maxResults, NodeQueryCallback callback)
+ {
+ List nodes = null;
+ NodeQueryRowHandler rowHandler = new NodeQueryRowHandler(callback);
+
+ if(nodeParameters.getIncludeTypeIds() == null && nodeParameters.getIncludeNodeTypes() != null)
+ {
+ Set qnamesIds = qnameDAO.convertQNamesToIds(nodeParameters.getIncludeNodeTypes(), false);
+ nodeParameters.setIncludeTypeIds(new ArrayList(qnamesIds));
+ }
+
+ if(nodeParameters.getExcludeTypeIds() == null && nodeParameters.getExcludeNodeTypes() != null)
+ {
+ Set qnamesIds = qnameDAO.convertQNamesToIds(nodeParameters.getExcludeNodeTypes(), false);
+ nodeParameters.setExcludeTypeIds(new ArrayList(qnamesIds));
+ }
+
+ if(nodeParameters.getExcludeAspectIds() == null && nodeParameters.getExcludeAspects() != null)
+ {
+ Set qnamesIds = qnameDAO.convertQNamesToIds(nodeParameters.getExcludeAspects(), false);
+ nodeParameters.setExcludeAspectIds(new ArrayList(qnamesIds));
+ }
+
+ if(nodeParameters.getIncludeAspectIds() == null && nodeParameters.getIncludeAspects() != null)
+ {
+ Set qnamesIds = qnameDAO.convertQNamesToIds(nodeParameters.getIncludeAspects(), false);
+ nodeParameters.setIncludeAspectIds(new ArrayList(qnamesIds));
+ }
+
+ if(maxResults != 0 && maxResults != Integer.MAX_VALUE)
+ {
+ nodes = (List)template.queryForList(SELECT_NODES, nodeParameters, 0, maxResults);
+ }
+ else
+ {
+ nodes = (List)template.queryForList(SELECT_NODES, nodeParameters);
+ }
+
+ for(NodeEntity node : nodes)
+ {
+ rowHandler.processResult(node);
+ }
+ }
+
+ /**
+ * Class that passes results from a result entity into the client callback
+ */
+ protected class NodeQueryRowHandler
+ {
+ private final NodeQueryCallback callback;
+ private boolean more;
+
+ private NodeQueryRowHandler(NodeQueryCallback callback)
+ {
+ this.callback = callback;
+ this.more = true;
+ }
+
+ public void processResult(Node row)
+ {
+ if (!more)
+ {
+ // No more results required
+ return;
+ }
+
+ more = callback.handleNode(row);
+ }
+ }
+}