CAY-1966 SQLTemplate/SQLSelect positional parameter binding

* abstracting Velocity work in an injectable SQLTemplateProcessor service
* more utest cleanup


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/5aedf54e
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/5aedf54e
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/5aedf54e

Branch: refs/heads/master
Commit: 5aedf54ed7b420e3c92bb6add3fcb9619aecce5c
Parents: c870954
Author: aadamchik <[email protected]>
Authored: Sun Nov 2 15:49:38 2014 +0300
Committer: aadamchik <[email protected]>
Committed: Sun Nov 2 16:51:54 2014 +0300

----------------------------------------------------------------------
 .../org/apache/cayenne/access/DataNode.java     |  16 +
 .../cayenne/access/jdbc/SQLTemplateAction.java  |   5 +-
 .../access/jdbc/SQLTemplateProcessor.java       |  33 ++
 .../server/DefaultDataNodeFactory.java          |   5 +
 .../configuration/server/ServerModule.java      |   4 +
 .../apache/cayenne/velocity/BindDirective.java  |   2 +-
 .../cayenne/velocity/ResultDirective.java       |   2 +-
 .../cayenne/velocity/SQLTemplateProcessor.java  | 155 ---------
 .../velocity/VelocitySQLTemplateProcessor.java  | 146 ++++++++
 .../server/DataDomainProviderTest.java          | 346 ++++++++++---------
 .../di/server/ServerCaseDataNodeFactory.java    |   5 +
 .../cayenne/velocity/BindDirectiveIT.java       |  33 +-
 .../cayenne/velocity/ResultDirectiveIT.java     | 266 +++++++-------
 .../velocity/SQLTemplateProcessorChainTest.java | 221 ------------
 .../SQLTemplateProcessorSelectTest.java         | 112 ------
 .../velocity/SQLTemplateProcessorTest.java      | 235 -------------
 .../VelocitySQLTemplateProcessorTest.java       | 234 +++++++++++++
 .../VelocitySQLTemplateProcessor_ChainTest.java | 184 ++++++++++
 ...VelocitySQLTemplateProcessor_SelectTest.java | 109 ++++++
 19 files changed, 1047 insertions(+), 1066 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/main/java/org/apache/cayenne/access/DataNode.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/DataNode.java 
b/cayenne-server/src/main/java/org/apache/cayenne/access/DataNode.java
index 907c81a..4d1b5f0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataNode.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataNode.java
@@ -36,6 +36,7 @@ import org.apache.cayenne.access.dbsync.SchemaUpdateStrategy;
 import org.apache.cayenne.access.dbsync.SkipSchemaUpdateStrategy;
 import org.apache.cayenne.access.jdbc.ColumnDescriptor;
 import org.apache.cayenne.access.jdbc.RowDescriptor;
+import org.apache.cayenne.access.jdbc.SQLTemplateProcessor;
 import org.apache.cayenne.access.jdbc.reader.RowReader;
 import org.apache.cayenne.access.jdbc.reader.RowReaderFactory;
 import org.apache.cayenne.access.translator.batch.BatchTranslator;
@@ -74,6 +75,7 @@ public class DataNode implements QueryEngine {
     private JdbcEventLogger jdbcEventLogger;
     private RowReaderFactory rowReaderFactory;
     private BatchTranslatorFactory batchTranslatorFactory;
+    private SQLTemplateProcessor sqlTemplateProcessor;
 
     TransactionDataSource readThroughDataSource;
 
@@ -504,4 +506,18 @@ public class DataNode implements QueryEngine {
     public void setBatchTranslatorFactory(BatchTranslatorFactory 
batchTranslatorFactory) {
         this.batchTranslatorFactory = batchTranslatorFactory;
     }
+
+    /**
+     * @since 4.0
+     */
+       public SQLTemplateProcessor getSqlTemplateProcessor() {
+               return sqlTemplateProcessor;
+       }
+
+       /**
+        * @since 4.0
+        */
+       public void setSqlTemplateProcessor(SQLTemplateProcessor 
sqlTemplateProcessor) {
+               this.sqlTemplateProcessor = sqlTemplateProcessor;
+       }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
index 50e8691..2e0d767 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java
@@ -49,7 +49,6 @@ import org.apache.cayenne.query.QueryMetadata;
 import org.apache.cayenne.query.SQLAction;
 import org.apache.cayenne.query.SQLTemplate;
 import org.apache.cayenne.util.Util;
-import org.apache.cayenne.velocity.SQLTemplateProcessor;
 import org.apache.commons.collections.IteratorUtils;
 
 /**
@@ -104,8 +103,6 @@ public class SQLTemplateAction implements SQLAction {
                boolean loggable = dataNode.getJdbcEventLogger().isLoggable();
                int size = query.parametersSize();
 
-               SQLTemplateProcessor templateProcessor = new 
SQLTemplateProcessor();
-
                // zero size indicates a one-shot query with no parameters
                // so fake a single entry batch...
                int batchSize = (size > 0) ? size : 1;
@@ -119,7 +116,7 @@ public class SQLTemplateAction implements SQLAction {
                for (int i = 0; i < batchSize; i++) {
                        Map<String, ?> nextParameters = it.next();
 
-                       SQLStatement compiled = 
templateProcessor.processTemplate(template, nextParameters);
+                       SQLStatement compiled = 
dataNode.getSqlTemplateProcessor().processTemplate(template, nextParameters);
 
                        if (loggable) {
                                
dataNode.getJdbcEventLogger().logQuery(compiled.getSql(), 
Arrays.asList(compiled.getBindings()));

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java
new file mode 100644
index 0000000..3873494
--- /dev/null
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java
@@ -0,0 +1,33 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.access.jdbc;
+
+import java.util.Map;
+
+/**
+ * @since 4.0
+ */
+public interface SQLTemplateProcessor {
+
+       /**
+        * Builds and returns a SQLStatement based on SQL template String and a 
map
+        * of parameters.
+        */
+       SQLStatement processTemplate(String template, Map<String, ?> 
parameters);
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DefaultDataNodeFactory.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DefaultDataNodeFactory.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DefaultDataNodeFactory.java
index bd844d4..4acaa9e 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DefaultDataNodeFactory.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DefaultDataNodeFactory.java
@@ -22,6 +22,7 @@ import javax.sql.DataSource;
 
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.dbsync.SchemaUpdateStrategy;
+import org.apache.cayenne.access.jdbc.SQLTemplateProcessor;
 import org.apache.cayenne.access.jdbc.reader.RowReaderFactory;
 import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
 import org.apache.cayenne.configuration.DataNodeDescriptor;
@@ -54,6 +55,9 @@ public class DefaultDataNodeFactory implements 
DataNodeFactory {
 
     @Inject
     protected SchemaUpdateStrategy defaultSchemaUpdateStrategy;
+    
+    @Inject
+    protected SQLTemplateProcessor sqlTemplateProcessor;
 
     @Override
     public DataNode createDataNode(DataNodeDescriptor nodeDescriptor) throws 
Exception {
@@ -63,6 +67,7 @@ public class DefaultDataNodeFactory implements 
DataNodeFactory {
         dataNode.setJdbcEventLogger(jdbcEventLogger);
         dataNode.setRowReaderFactory(rowReaderFactory);
         dataNode.setBatchTranslatorFactory(batchTranslatorFactory);
+        dataNode.setSqlTemplateProcessor(sqlTemplateProcessor);
 
         dataNode.setDataSourceLocation(nodeDescriptor.getParameters());
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
index 7e1cd74..8119982 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
@@ -27,6 +27,7 @@ import 
org.apache.cayenne.access.DefaultObjectMapRetainStrategy;
 import org.apache.cayenne.access.ObjectMapRetainStrategy;
 import org.apache.cayenne.access.dbsync.SchemaUpdateStrategy;
 import org.apache.cayenne.access.dbsync.SkipSchemaUpdateStrategy;
+import org.apache.cayenne.access.jdbc.SQLTemplateProcessor;
 import org.apache.cayenne.access.jdbc.reader.DefaultRowReaderFactory;
 import org.apache.cayenne.access.jdbc.reader.RowReaderFactory;
 import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
@@ -98,6 +99,7 @@ import org.apache.cayenne.tx.DefaultTransactionFactory;
 import org.apache.cayenne.tx.DefaultTransactionManager;
 import org.apache.cayenne.tx.TransactionFactory;
 import org.apache.cayenne.tx.TransactionManager;
+import org.apache.cayenne.velocity.VelocitySQLTemplateProcessor;
 
 /**
  * A DI module containing all Cayenne server runtime configuration.
@@ -265,5 +267,7 @@ public class ServerModule implements Module {
         
         
binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
         binder.bind(RowReaderFactory.class).to(DefaultRowReaderFactory.class);
+        
+        
binder.bind(SQLTemplateProcessor.class).to(VelocitySQLTemplateProcessor.class);
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindDirective.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindDirective.java 
b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindDirective.java
index 3ff81b7..6e7e83e 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindDirective.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/velocity/BindDirective.java
@@ -167,7 +167,7 @@ public class BindDirective extends Directive {
     protected void bind(InternalContextAdapter context, ParameterBinding 
binding) {
 
         Collection bindings = (Collection) 
context.getInternalUserContext().get(
-                SQLTemplateProcessor.BINDINGS_LIST_KEY);
+                VelocitySQLTemplateProcessor.BINDINGS_LIST_KEY);
 
         if (bindings != null) {
             bindings.add(binding);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/main/java/org/apache/cayenne/velocity/ResultDirective.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/velocity/ResultDirective.java 
b/cayenne-server/src/main/java/org/apache/cayenne/velocity/ResultDirective.java
index 8a3a5e4..315c56b 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/velocity/ResultDirective.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/velocity/ResultDirective.java
@@ -197,7 +197,7 @@ public class ResultDirective extends Directive {
 
         Collection<Object> resultColumns = (Collection<Object>) context
                 .getInternalUserContext()
-                .get(SQLTemplateProcessor.RESULT_COLUMNS_LIST_KEY);
+                .get(VelocitySQLTemplateProcessor.RESULT_COLUMNS_LIST_KEY);
 
         if (resultColumns != null) {
             resultColumns.add(columnDescriptor);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateProcessor.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateProcessor.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateProcessor.java
deleted file mode 100644
index 71974d3..0000000
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateProcessor.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.velocity;
-
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.access.jdbc.ColumnDescriptor;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
-import org.apache.cayenne.access.jdbc.SQLStatement;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.context.InternalContextAdapterImpl;
-import org.apache.velocity.runtime.RuntimeConstants;
-import org.apache.velocity.runtime.RuntimeInstance;
-import org.apache.velocity.runtime.log.NullLogChute;
-import org.apache.velocity.runtime.parser.ParseException;
-import org.apache.velocity.runtime.parser.node.SimpleNode;
-
-/**
- * Processor for SQL velocity templates.
- * 
- * @see org.apache.cayenne.query.SQLTemplate
- * @since 4.0
- */
-public class SQLTemplateProcessor {
-
-       private static RuntimeInstance sharedRuntime;
-
-       static final String BINDINGS_LIST_KEY = "bindings";
-       static final String RESULT_COLUMNS_LIST_KEY = "resultColumns";
-       static final String HELPER_KEY = "helper";
-
-       private static final SQLTemplateRenderingUtils sharedUtils = new 
SQLTemplateRenderingUtils();
-
-       RuntimeInstance velocityRuntime;
-       SQLTemplateRenderingUtils renderingUtils;
-
-       static {
-               initVelocityRuntime();
-       }
-
-       private static void initVelocityRuntime() {
-               // init static velocity engine
-               sharedRuntime = new RuntimeInstance();
-
-               // set null logger
-               
sharedRuntime.addProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new 
NullLogChute());
-
-               
sharedRuntime.addProperty(RuntimeConstants.RESOURCE_MANAGER_CLASS, 
SQLTemplateResourceManager.class.getName());
-               sharedRuntime.addProperty("userdirective", 
BindDirective.class.getName());
-               sharedRuntime.addProperty("userdirective", 
BindEqualDirective.class.getName());
-               sharedRuntime.addProperty("userdirective", 
BindNotEqualDirective.class.getName());
-               sharedRuntime.addProperty("userdirective", 
BindObjectEqualDirective.class.getName());
-               sharedRuntime.addProperty("userdirective", 
BindObjectNotEqualDirective.class.getName());
-               sharedRuntime.addProperty("userdirective", 
ResultDirective.class.getName());
-               sharedRuntime.addProperty("userdirective", 
ChainDirective.class.getName());
-               sharedRuntime.addProperty("userdirective", 
ChunkDirective.class.getName());
-               try {
-                       sharedRuntime.init();
-               } catch (Exception ex) {
-                       throw new CayenneRuntimeException("Error setting up 
Velocity RuntimeInstance.", ex);
-               }
-       }
-
-       public SQLTemplateProcessor() {
-               this.velocityRuntime = sharedRuntime;
-               this.renderingUtils = sharedUtils;
-       }
-
-       public SQLTemplateProcessor(RuntimeInstance velocityRuntime, 
SQLTemplateRenderingUtils renderingUtils) {
-               this.velocityRuntime = velocityRuntime;
-               this.renderingUtils = renderingUtils;
-       }
-
-       /**
-        * Builds and returns a SQLStatement based on SQL template and a set of
-        * parameters. During rendering, VelocityContext exposes the following 
as
-        * variables: all parameters in the map, {@link 
SQLTemplateRenderingUtils}
-        * as a "helper" variable and SQLStatement object as "statement" 
variable.
-        */
-       public SQLStatement processTemplate(String template, Map<String, ?> 
parameters) throws Exception {
-               // have to make a copy of parameter map since we are gonna 
modify it..
-               Map<String, Object> internalParameters = (parameters != null && 
!parameters.isEmpty()) ? new HashMap<String, Object>(
-                               parameters) : new HashMap<String, Object>(5);
-
-               List<ParameterBinding> bindings = new 
ArrayList<ParameterBinding>();
-               List<ColumnDescriptor> results = new 
ArrayList<ColumnDescriptor>();
-               internalParameters.put(BINDINGS_LIST_KEY, bindings);
-               internalParameters.put(RESULT_COLUMNS_LIST_KEY, results);
-               internalParameters.put(HELPER_KEY, renderingUtils);
-
-               String sql = buildStatement(new 
VelocityContext(internalParameters), template);
-
-               ParameterBinding[] bindingsArray = new 
ParameterBinding[bindings.size()];
-               bindings.toArray(bindingsArray);
-
-               ColumnDescriptor[] resultsArray = new 
ColumnDescriptor[results.size()];
-               results.toArray(resultsArray);
-
-               return new SQLStatement(sql, resultsArray, bindingsArray);
-       }
-
-       String buildStatement(VelocityContext context, String template) throws 
Exception {
-               // Note: this method is a reworked version of
-               // org.apache.velocity.app.Velocity.evaluate(..)
-               // cleaned up to avoid using any Velocity singletons
-
-               StringWriter out = new StringWriter(template.length());
-               SimpleNode nodeTree = null;
-
-               try {
-                       nodeTree = velocityRuntime.parse(new 
StringReader(template), template);
-               } catch (ParseException pex) {
-                       throw new CayenneRuntimeException("Error parsing 
template '" + template + "' : " + pex.getMessage());
-               }
-
-               if (nodeTree == null) {
-                       throw new CayenneRuntimeException("Error parsing 
template " + template);
-               }
-
-               // ... not sure what InternalContextAdapter is for...
-               InternalContextAdapterImpl ica = new 
InternalContextAdapterImpl(context);
-               ica.pushCurrentTemplateName(template);
-
-               try {
-                       nodeTree.init(ica, velocityRuntime);
-                       nodeTree.render(ica, out);
-                       return out.toString();
-               } finally {
-                       ica.popCurrentTemplateName();
-               }
-       }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessor.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessor.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessor.java
new file mode 100644
index 0000000..89e7952
--- /dev/null
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateProcessor.java
@@ -0,0 +1,146 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.velocity;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.access.jdbc.ColumnDescriptor;
+import org.apache.cayenne.access.jdbc.ParameterBinding;
+import org.apache.cayenne.access.jdbc.SQLStatement;
+import org.apache.cayenne.access.jdbc.SQLTemplateProcessor;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.context.InternalContextAdapterImpl;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.RuntimeInstance;
+import org.apache.velocity.runtime.log.NullLogChute;
+import org.apache.velocity.runtime.parser.ParseException;
+import org.apache.velocity.runtime.parser.node.SimpleNode;
+
+/**
+ * Processor for SQL velocity templates.
+ * 
+ * @see org.apache.cayenne.query.SQLTemplate
+ * @since 4.0
+ */
+public class VelocitySQLTemplateProcessor implements SQLTemplateProcessor {
+
+       static final String BINDINGS_LIST_KEY = "bindings";
+       static final String RESULT_COLUMNS_LIST_KEY = "resultColumns";
+       static final String HELPER_KEY = "helper";
+
+       protected RuntimeInstance velocityRuntime;
+       protected SQLTemplateRenderingUtils renderingUtils;
+
+       public VelocitySQLTemplateProcessor() {
+               this.renderingUtils = new SQLTemplateRenderingUtils();
+               this.velocityRuntime = new RuntimeInstance();
+
+               // set null logger
+               
velocityRuntime.addProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new 
NullLogChute());
+
+               velocityRuntime
+                               
.addProperty(RuntimeConstants.RESOURCE_MANAGER_CLASS, 
SQLTemplateResourceManager.class.getName());
+               velocityRuntime.addProperty("userdirective", 
BindDirective.class.getName());
+               velocityRuntime.addProperty("userdirective", 
BindEqualDirective.class.getName());
+               velocityRuntime.addProperty("userdirective", 
BindNotEqualDirective.class.getName());
+               velocityRuntime.addProperty("userdirective", 
BindObjectEqualDirective.class.getName());
+               velocityRuntime.addProperty("userdirective", 
BindObjectNotEqualDirective.class.getName());
+               velocityRuntime.addProperty("userdirective", 
ResultDirective.class.getName());
+               velocityRuntime.addProperty("userdirective", 
ChainDirective.class.getName());
+               velocityRuntime.addProperty("userdirective", 
ChunkDirective.class.getName());
+               try {
+                       velocityRuntime.init();
+               } catch (Exception ex) {
+                       throw new CayenneRuntimeException("Error setting up 
Velocity RuntimeInstance.", ex);
+               }
+
+       }
+
+       /**
+        * Builds and returns a SQLStatement based on SQL template and a set of
+        * parameters. During rendering, VelocityContext exposes the following 
as
+        * variables: all parameters in the map, {@link 
SQLTemplateRenderingUtils}
+        * as a "helper" variable and SQLStatement object as "statement" 
variable.
+        */
+       @Override
+       public SQLStatement processTemplate(String template, Map<String, ?> 
parameters) {
+               // have to make a copy of parameter map since we are gonna 
modify it..
+               Map<String, Object> internalParameters = (parameters != null && 
!parameters.isEmpty()) ? new HashMap<String, Object>(
+                               parameters) : new HashMap<String, Object>(5);
+
+               List<ParameterBinding> bindings = new 
ArrayList<ParameterBinding>();
+               List<ColumnDescriptor> results = new 
ArrayList<ColumnDescriptor>();
+               internalParameters.put(BINDINGS_LIST_KEY, bindings);
+               internalParameters.put(RESULT_COLUMNS_LIST_KEY, results);
+               internalParameters.put(HELPER_KEY, renderingUtils);
+
+               String sql;
+               try {
+                       sql = buildStatement(new 
VelocityContext(internalParameters), template);
+               } catch (Exception e) {
+                       throw new CayenneRuntimeException("Error processing 
Velocity template", e);
+               }
+
+               ParameterBinding[] bindingsArray = new 
ParameterBinding[bindings.size()];
+               bindings.toArray(bindingsArray);
+
+               ColumnDescriptor[] resultsArray = new 
ColumnDescriptor[results.size()];
+               results.toArray(resultsArray);
+
+               return new SQLStatement(sql, resultsArray, bindingsArray);
+       }
+
+       String buildStatement(VelocityContext context, String template) throws 
Exception {
+               // Note: this method is a reworked version of
+               // org.apache.velocity.app.Velocity.evaluate(..)
+               // cleaned up to avoid using any Velocity singletons
+
+               StringWriter out = new StringWriter(template.length());
+               SimpleNode nodeTree = null;
+
+               try {
+                       nodeTree = velocityRuntime.parse(new 
StringReader(template), template);
+               } catch (ParseException pex) {
+                       throw new CayenneRuntimeException("Error parsing 
template '" + template + "' : " + pex.getMessage());
+               }
+
+               if (nodeTree == null) {
+                       throw new CayenneRuntimeException("Error parsing 
template " + template);
+               }
+
+               // ... not sure what InternalContextAdapter is for...
+               InternalContextAdapterImpl ica = new 
InternalContextAdapterImpl(context);
+               ica.pushCurrentTemplateName(template);
+
+               try {
+                       nodeTree.init(ica, velocityRuntime);
+                       nodeTree.render(ica, out);
+                       return out.toString();
+               } finally {
+                       ica.popCurrentTemplateName();
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
index fbbbce9..69b5164 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
@@ -18,6 +18,16 @@
  ****************************************************************/
 package org.apache.cayenne.configuration.server;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.util.Collection;
+import java.util.Collections;
+
 import org.apache.cayenne.ConfigurationException;
 import org.apache.cayenne.DataChannel;
 import org.apache.cayenne.access.DataDomain;
@@ -25,6 +35,7 @@ import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.dbsync.SchemaUpdateStrategy;
 import org.apache.cayenne.access.dbsync.SkipSchemaUpdateStrategy;
 import org.apache.cayenne.access.dbsync.ThrowOnPartialOrCreateSchemaStrategy;
+import org.apache.cayenne.access.jdbc.SQLTemplateProcessor;
 import org.apache.cayenne.access.jdbc.reader.RowReaderFactory;
 import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
 import 
org.apache.cayenne.access.translator.batch.DefaultBatchTranslatorFactory;
@@ -77,178 +88,169 @@ import org.apache.cayenne.resource.ResourceLocator;
 import org.apache.cayenne.resource.mock.MockResource;
 import org.junit.Test;
 
-import java.util.Collection;
-import java.util.Collections;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-
 public class DataDomainProviderTest {
 
-    @Test
-    public void testGet() {
-
-        // create dependencies
-        final String testConfigName = "testConfig";
-        final DataChannelDescriptor testDescriptor = new 
DataChannelDescriptor();
-
-        DataMap map1 = new DataMap("map1");
-        testDescriptor.getDataMaps().add(map1);
-
-        DataMap map2 = new DataMap("map2");
-        testDescriptor.getDataMaps().add(map2);
-
-        DataNodeDescriptor nodeDescriptor1 = new DataNodeDescriptor();
-        nodeDescriptor1.setName("node1");
-        nodeDescriptor1.getDataMapNames().add("map1");
-        nodeDescriptor1.setAdapterType(OracleAdapter.class.getName());
-        
nodeDescriptor1.setDataSourceFactoryType(MockDataSourceFactory.class.getName());
-        nodeDescriptor1.setParameters("jdbc/testDataNode1");
-        
nodeDescriptor1.setSchemaUpdateStrategyType(ThrowOnPartialOrCreateSchemaStrategy.class.getName());
-        testDescriptor.getNodeDescriptors().add(nodeDescriptor1);
-
-        DataNodeDescriptor nodeDescriptor2 = new DataNodeDescriptor();
-        nodeDescriptor2.setName("node2");
-        nodeDescriptor2.getDataMapNames().add("map2");
-        nodeDescriptor2.setParameters("testDataNode2.driver.xml");
-        testDescriptor.getNodeDescriptors().add(nodeDescriptor2);
-
-        final DataChannelDescriptorLoader testLoader = new 
DataChannelDescriptorLoader() {
-
-            @Override
-            public ConfigurationTree<DataChannelDescriptor> load(Resource 
configurationResource)
-                    throws ConfigurationException {
-                return new 
ConfigurationTree<DataChannelDescriptor>(testDescriptor, null);
-            }
-        };
-
-        final EventManager eventManager = new MockEventManager();
-
-        Module testModule = new Module() {
-
-            @Override
-            public void configure(Binder binder) {
-                final ClassLoaderManager classLoaderManager = new 
DefaultClassLoaderManager();
-                
binder.bind(ClassLoaderManager.class).toInstance(classLoaderManager);
-                
binder.bind(AdhocObjectFactory.class).to(DefaultAdhocObjectFactory.class);
-
-                binder.bindMap(Constants.PROPERTIES_MAP);
-
-                binder.bind(FirebirdSniffer.class).to(FirebirdSniffer.class);
-                binder.bind(OpenBaseSniffer.class).to(OpenBaseSniffer.class);
-                binder.bind(FrontBaseSniffer.class).to(FrontBaseSniffer.class);
-                binder.bind(IngresSniffer.class).to(IngresSniffer.class);
-                binder.bind(SQLiteSniffer.class).to(SQLiteSniffer.class);
-                binder.bind(DB2Sniffer.class).to(DB2Sniffer.class);
-                binder.bind(H2Sniffer.class).to(H2Sniffer.class);
-                binder.bind(HSQLDBSniffer.class).to(HSQLDBSniffer.class);
-                binder.bind(SybaseSniffer.class).to(SybaseSniffer.class);
-                binder.bind(DerbySniffer.class).to(DerbySniffer.class);
-                binder.bind(SQLServerSniffer.class).to(SQLServerSniffer.class);
-                binder.bind(OracleSniffer.class).to(OracleSniffer.class);
-                binder.bind(PostgresSniffer.class).to(PostgresSniffer.class);
-                binder.bind(MySQLSniffer.class).to(MySQLSniffer.class);
-
-                
binder.bindList(Constants.SERVER_ADAPTER_DETECTORS_LIST).add(FirebirdSniffer.class)
-                        
.add(OpenBaseSniffer.class).add(FrontBaseSniffer.class).add(IngresSniffer.class)
-                        
.add(SQLiteSniffer.class).add(DB2Sniffer.class).add(H2Sniffer.class).add(HSQLDBSniffer.class)
-                        
.add(SybaseSniffer.class).add(DerbySniffer.class).add(SQLServerSniffer.class)
-                        
.add(OracleSniffer.class).add(PostgresSniffer.class).add(MySQLSniffer.class);
-                binder.bindList(Constants.SERVER_DOMAIN_FILTERS_LIST);
-                
binder.bindList(Constants.SERVER_PROJECT_LOCATIONS_LIST).add(testConfigName);
-
-                // configure extended types
-                binder.bindList(Constants.SERVER_DEFAULT_TYPES_LIST);
-                binder.bindList(Constants.SERVER_USER_TYPES_LIST);
-                binder.bindList(Constants.SERVER_TYPE_FACTORIES_LIST);
-
-                binder.bind(EventManager.class).toInstance(eventManager);
-                binder.bind(EntitySorter.class).toInstance(new 
AshwoodEntitySorter());
-
-                final ResourceLocator locator = new 
ClassLoaderResourceLocator(classLoaderManager) {
-
-                    public Collection<Resource> findResources(String name) {
-                        // ResourceLocator also used by JdbcAdapter to locate
-                        // types.xml... if this is the request we are getting,
-                        // just let
-                        // it go through..
-                        if (name.endsWith("types.xml")) {
-                            return super.findResources(name);
-                        }
-
-                        assertEquals(testConfigName, name);
-                        return Collections.<Resource> singleton(new 
MockResource());
-                    }
-                };
-
-                binder.bind(ResourceLocator.class).toInstance(locator);
-                
binder.bind(ConfigurationNameMapper.class).to(DefaultConfigurationNameMapper.class);
-                
binder.bind(DataChannelDescriptorMerger.class).to(DefaultDataChannelDescriptorMerger.class);
-                
binder.bind(DataChannelDescriptorLoader.class).toInstance(testLoader);
-                binder.bind(SchemaUpdateStrategy.class).toInstance(new 
SkipSchemaUpdateStrategy());
-                
binder.bind(DbAdapterFactory.class).to(DefaultDbAdapterFactory.class);
-                
binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-                
binder.bind(BatchTranslatorFactory.class).to(DefaultBatchTranslatorFactory.class);
-
-                binder.bind(DataSourceFactory.class).toInstance(new 
MockDataSourceFactory());
-                
binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
-                
binder.bind(QueryCache.class).toInstance(mock(QueryCache.class));
-                
binder.bind(RowReaderFactory.class).toInstance(mock(RowReaderFactory.class));
-                
binder.bind(DataNodeFactory.class).to(DefaultDataNodeFactory.class);
-            }
-        };
-
-        Injector injector = DIBootstrap.createInjector(testModule);
-
-        // create and initialize provide instance to test
-        DataDomainProvider provider = new DataDomainProvider();
-        injector.injectMembers(provider);
-
-        DataChannel channel = provider.get();
-        assertNotNull(channel);
-
-        assertTrue(channel instanceof DataDomain);
-
-        DataDomain domain = (DataDomain) channel;
-        assertSame(eventManager, domain.getEventManager());
-        assertEquals(2, domain.getDataMaps().size());
-        assertTrue(domain.getDataMaps().contains(map1));
-        assertTrue(domain.getDataMaps().contains(map2));
-
-        assertEquals(2, domain.getDataNodes().size());
-        DataNode node1 = domain.getDataNode("node1");
-        assertNotNull(node1);
-        assertEquals(1, node1.getDataMaps().size());
-        assertSame(map1, node1.getDataMaps().iterator().next());
-        assertSame(node1, domain.lookupDataNode(map1));
-        assertEquals(nodeDescriptor1.getDataSourceFactoryType(), 
node1.getDataSourceFactory());
-        assertNotNull(node1.getDataSource());
-        assertEquals(nodeDescriptor1.getParameters(), 
node1.getDataSourceLocation());
-
-        assertEquals(nodeDescriptor1.getSchemaUpdateStrategyType(), 
node1.getSchemaUpdateStrategyName());
-        assertNotNull(node1.getSchemaUpdateStrategy());
-        assertEquals(nodeDescriptor1.getSchemaUpdateStrategyType(), 
node1.getSchemaUpdateStrategy().getClass()
-                .getName());
-
-        assertNotNull(node1.getAdapter());
-        assertEquals(OracleAdapter.class, node1.getAdapter().getClass());
-
-        DataNode node2 = domain.getDataNode("node2");
-        assertNotNull(node2);
-        assertEquals(1, node2.getDataMaps().size());
-        assertSame(map2, node2.getDataMaps().iterator().next());
-        assertSame(node2, domain.lookupDataNode(map2));
-        assertNull(node2.getDataSourceFactory());
-        assertNotNull(node2.getDataSource());
-        assertEquals(nodeDescriptor2.getParameters(), 
node2.getDataSourceLocation());
-        assertEquals(SkipSchemaUpdateStrategy.class.getName(), 
node2.getSchemaUpdateStrategyName());
-        assertNotNull(node2.getSchemaUpdateStrategy());
-        assertEquals(SkipSchemaUpdateStrategy.class.getName(), 
node2.getSchemaUpdateStrategy().getClass().getName());
-
-        assertNotNull(node2.getAdapter());
-    }
+       @Test
+       public void testGet() {
+
+               // create dependencies
+               final String testConfigName = "testConfig";
+               final DataChannelDescriptor testDescriptor = new 
DataChannelDescriptor();
+
+               DataMap map1 = new DataMap("map1");
+               testDescriptor.getDataMaps().add(map1);
+
+               DataMap map2 = new DataMap("map2");
+               testDescriptor.getDataMaps().add(map2);
+
+               DataNodeDescriptor nodeDescriptor1 = new DataNodeDescriptor();
+               nodeDescriptor1.setName("node1");
+               nodeDescriptor1.getDataMapNames().add("map1");
+               nodeDescriptor1.setAdapterType(OracleAdapter.class.getName());
+               
nodeDescriptor1.setDataSourceFactoryType(MockDataSourceFactory.class.getName());
+               nodeDescriptor1.setParameters("jdbc/testDataNode1");
+               
nodeDescriptor1.setSchemaUpdateStrategyType(ThrowOnPartialOrCreateSchemaStrategy.class.getName());
+               testDescriptor.getNodeDescriptors().add(nodeDescriptor1);
+
+               DataNodeDescriptor nodeDescriptor2 = new DataNodeDescriptor();
+               nodeDescriptor2.setName("node2");
+               nodeDescriptor2.getDataMapNames().add("map2");
+               nodeDescriptor2.setParameters("testDataNode2.driver.xml");
+               testDescriptor.getNodeDescriptors().add(nodeDescriptor2);
+
+               final DataChannelDescriptorLoader testLoader = new 
DataChannelDescriptorLoader() {
+
+                       @Override
+                       public ConfigurationTree<DataChannelDescriptor> 
load(Resource configurationResource)
+                                       throws ConfigurationException {
+                               return new 
ConfigurationTree<DataChannelDescriptor>(testDescriptor, null);
+                       }
+               };
+
+               final EventManager eventManager = new MockEventManager();
+
+               Module testModule = new Module() {
+
+                       @Override
+                       public void configure(Binder binder) {
+                               final ClassLoaderManager classLoaderManager = 
new DefaultClassLoaderManager();
+                               
binder.bind(ClassLoaderManager.class).toInstance(classLoaderManager);
+                               
binder.bind(AdhocObjectFactory.class).to(DefaultAdhocObjectFactory.class);
+
+                               binder.bindMap(Constants.PROPERTIES_MAP);
+
+                               
binder.bind(FirebirdSniffer.class).to(FirebirdSniffer.class);
+                               
binder.bind(OpenBaseSniffer.class).to(OpenBaseSniffer.class);
+                               
binder.bind(FrontBaseSniffer.class).to(FrontBaseSniffer.class);
+                               
binder.bind(IngresSniffer.class).to(IngresSniffer.class);
+                               
binder.bind(SQLiteSniffer.class).to(SQLiteSniffer.class);
+                               
binder.bind(DB2Sniffer.class).to(DB2Sniffer.class);
+                               
binder.bind(H2Sniffer.class).to(H2Sniffer.class);
+                               
binder.bind(HSQLDBSniffer.class).to(HSQLDBSniffer.class);
+                               
binder.bind(SybaseSniffer.class).to(SybaseSniffer.class);
+                               
binder.bind(DerbySniffer.class).to(DerbySniffer.class);
+                               
binder.bind(SQLServerSniffer.class).to(SQLServerSniffer.class);
+                               
binder.bind(OracleSniffer.class).to(OracleSniffer.class);
+                               
binder.bind(PostgresSniffer.class).to(PostgresSniffer.class);
+                               
binder.bind(MySQLSniffer.class).to(MySQLSniffer.class);
+
+                               
binder.bindList(Constants.SERVER_ADAPTER_DETECTORS_LIST).add(FirebirdSniffer.class)
+                                               
.add(OpenBaseSniffer.class).add(FrontBaseSniffer.class).add(IngresSniffer.class)
+                                               
.add(SQLiteSniffer.class).add(DB2Sniffer.class).add(H2Sniffer.class).add(HSQLDBSniffer.class)
+                                               
.add(SybaseSniffer.class).add(DerbySniffer.class).add(SQLServerSniffer.class)
+                                               
.add(OracleSniffer.class).add(PostgresSniffer.class).add(MySQLSniffer.class);
+                               
binder.bindList(Constants.SERVER_DOMAIN_FILTERS_LIST);
+                               
binder.bindList(Constants.SERVER_PROJECT_LOCATIONS_LIST).add(testConfigName);
+
+                               // configure extended types
+                               
binder.bindList(Constants.SERVER_DEFAULT_TYPES_LIST);
+                               
binder.bindList(Constants.SERVER_USER_TYPES_LIST);
+                               
binder.bindList(Constants.SERVER_TYPE_FACTORIES_LIST);
+
+                               
binder.bind(EventManager.class).toInstance(eventManager);
+                               binder.bind(EntitySorter.class).toInstance(new 
AshwoodEntitySorter());
+
+                               final ResourceLocator locator = new 
ClassLoaderResourceLocator(classLoaderManager) {
+
+                                       public Collection<Resource> 
findResources(String name) {
+                                               // ResourceLocator also used by 
JdbcAdapter to locate
+                                               // types.xml... if this is the 
request we are getting,
+                                               // just let
+                                               // it go through..
+                                               if (name.endsWith("types.xml")) 
{
+                                                       return 
super.findResources(name);
+                                               }
+
+                                               assertEquals(testConfigName, 
name);
+                                               return Collections.<Resource> 
singleton(new MockResource());
+                                       }
+                               };
+
+                               
binder.bind(ResourceLocator.class).toInstance(locator);
+                               
binder.bind(ConfigurationNameMapper.class).to(DefaultConfigurationNameMapper.class);
+                               
binder.bind(DataChannelDescriptorMerger.class).to(DefaultDataChannelDescriptorMerger.class);
+                               
binder.bind(DataChannelDescriptorLoader.class).toInstance(testLoader);
+                               
binder.bind(SchemaUpdateStrategy.class).toInstance(new 
SkipSchemaUpdateStrategy());
+                               
binder.bind(DbAdapterFactory.class).to(DefaultDbAdapterFactory.class);
+                               
binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
+                               
binder.bind(BatchTranslatorFactory.class).to(DefaultBatchTranslatorFactory.class);
+
+                               
binder.bind(DataSourceFactory.class).toInstance(new MockDataSourceFactory());
+                               
binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
+                               
binder.bind(QueryCache.class).toInstance(mock(QueryCache.class));
+                               
binder.bind(RowReaderFactory.class).toInstance(mock(RowReaderFactory.class));
+                               
binder.bind(DataNodeFactory.class).to(DefaultDataNodeFactory.class);
+                               
binder.bind(SQLTemplateProcessor.class).toInstance(mock(SQLTemplateProcessor.class));
+                       }
+               };
+
+               Injector injector = DIBootstrap.createInjector(testModule);
+
+               // create and initialize provide instance to test
+               DataDomainProvider provider = new DataDomainProvider();
+               injector.injectMembers(provider);
+
+               DataChannel channel = provider.get();
+               assertNotNull(channel);
+
+               assertTrue(channel instanceof DataDomain);
+
+               DataDomain domain = (DataDomain) channel;
+               assertSame(eventManager, domain.getEventManager());
+               assertEquals(2, domain.getDataMaps().size());
+               assertTrue(domain.getDataMaps().contains(map1));
+               assertTrue(domain.getDataMaps().contains(map2));
+
+               assertEquals(2, domain.getDataNodes().size());
+               DataNode node1 = domain.getDataNode("node1");
+               assertNotNull(node1);
+               assertEquals(1, node1.getDataMaps().size());
+               assertSame(map1, node1.getDataMaps().iterator().next());
+               assertSame(node1, domain.lookupDataNode(map1));
+               assertEquals(nodeDescriptor1.getDataSourceFactoryType(), 
node1.getDataSourceFactory());
+               assertNotNull(node1.getDataSource());
+               assertEquals(nodeDescriptor1.getParameters(), 
node1.getDataSourceLocation());
+
+               assertEquals(nodeDescriptor1.getSchemaUpdateStrategyType(), 
node1.getSchemaUpdateStrategyName());
+               assertNotNull(node1.getSchemaUpdateStrategy());
+               assertEquals(nodeDescriptor1.getSchemaUpdateStrategyType(), 
node1.getSchemaUpdateStrategy().getClass()
+                               .getName());
+
+               assertNotNull(node1.getAdapter());
+               assertEquals(OracleAdapter.class, 
node1.getAdapter().getClass());
+
+               DataNode node2 = domain.getDataNode("node2");
+               assertNotNull(node2);
+               assertEquals(1, node2.getDataMaps().size());
+               assertSame(map2, node2.getDataMaps().iterator().next());
+               assertSame(node2, domain.lookupDataNode(map2));
+               assertNull(node2.getDataSourceFactory());
+               assertNotNull(node2.getDataSource());
+               assertEquals(nodeDescriptor2.getParameters(), 
node2.getDataSourceLocation());
+               assertEquals(SkipSchemaUpdateStrategy.class.getName(), 
node2.getSchemaUpdateStrategyName());
+               assertNotNull(node2.getSchemaUpdateStrategy());
+               assertEquals(SkipSchemaUpdateStrategy.class.getName(), 
node2.getSchemaUpdateStrategy().getClass().getName());
+
+               assertNotNull(node2.getAdapter());
+       }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseDataNodeFactory.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseDataNodeFactory.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseDataNodeFactory.java
index 7686eae..85ab18f 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseDataNodeFactory.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseDataNodeFactory.java
@@ -20,6 +20,7 @@ package org.apache.cayenne.unit.di.server;
 
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.dbsync.SkipSchemaUpdateStrategy;
+import org.apache.cayenne.access.jdbc.SQLTemplateProcessor;
 import org.apache.cayenne.access.jdbc.reader.RowReaderFactory;
 import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
 import org.apache.cayenne.configuration.DataNodeDescriptor;
@@ -44,6 +45,9 @@ public class ServerCaseDataNodeFactory implements 
DataNodeFactory {
 
     @Inject
     private DbAdapter adapter;
+    
+    @Inject
+    protected SQLTemplateProcessor sqlTemplateProcessor;
 
     @Override
     public DataNode createDataNode(DataNodeDescriptor nodeDescriptor) throws 
Exception {
@@ -57,6 +61,7 @@ public class ServerCaseDataNodeFactory implements 
DataNodeFactory {
         
dataNode.setDataSource(dataSourceFactory.getDataSource(nodeDescriptor.getName()));
         dataNode.setAdapter(adapter);
         dataNode.setSchemaUpdateStrategy(new SkipSchemaUpdateStrategy());
+        dataNode.setSqlTemplateProcessor(sqlTemplateProcessor);
 
         return dataNode;
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/test/java/org/apache/cayenne/velocity/BindDirectiveIT.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/velocity/BindDirectiveIT.java 
b/cayenne-server/src/test/java/org/apache/cayenne/velocity/BindDirectiveIT.java
index 78e55e8..60f23d6 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/velocity/BindDirectiveIT.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/velocity/BindDirectiveIT.java
@@ -18,12 +18,22 @@
  ****************************************************************/
 package org.apache.cayenne.velocity;
 
+import java.sql.Connection;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.MockOperationObserver;
 import org.apache.cayenne.access.jdbc.SQLTemplateAction;
-import org.apache.cayenne.access.jdbc.reader.RowReaderFactory;
 import org.apache.cayenne.dba.JdbcAdapter;
 import org.apache.cayenne.dba.oracle.OracleAdapter;
 import org.apache.cayenne.di.Inject;
@@ -37,19 +47,6 @@ import org.apache.cayenne.unit.di.server.ServerCase;
 import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
 import org.apache.cayenne.unit.di.server.UseServerRuntime;
 
-import java.sql.Connection;
-import java.sql.Timestamp;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static org.mockito.Mockito.mock;
-
 /**
  * Tests BindDirective for passed null parameters and for not passed parameters
  */
@@ -70,6 +67,9 @@ public class BindDirectiveIT extends ServerCase {
 
     @Inject
     private JdbcEventLogger logger;
+    
+    @Inject
+    private DataNode node;
 
     @Override
     protected void setUpAfterInjection() throws Exception {
@@ -238,10 +238,7 @@ public class BindDirectiveIT extends ServerCase {
 
         template.setParams(parameters);
 
-        DataNode node = new DataNode();
-        node.setEntityResolver(context.getEntityResolver());
-        node.setRowReaderFactory(mock(RowReaderFactory.class));
-        node.setAdapter(adapter);
+    
         SQLTemplateAction action = new SQLTemplateAction(template, node);
 
         Connection c = dataSourceFactory.getSharedDataSource().getConnection();

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/test/java/org/apache/cayenne/velocity/ResultDirectiveIT.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/velocity/ResultDirectiveIT.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/velocity/ResultDirectiveIT.java
index 3c291b8..2e7049b 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/velocity/ResultDirectiveIT.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/velocity/ResultDirectiveIT.java
@@ -18,11 +18,16 @@
  ****************************************************************/
 package org.apache.cayenne.velocity;
 
+import java.sql.Connection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.MockOperationObserver;
 import org.apache.cayenne.access.jdbc.SQLTemplateAction;
-import org.apache.cayenne.access.jdbc.reader.RowReaderFactory;
 import org.apache.cayenne.configuration.server.ServerRuntime;
 import org.apache.cayenne.dba.JdbcAdapter;
 import org.apache.cayenne.di.Inject;
@@ -34,155 +39,122 @@ import org.apache.cayenne.testdo.testmap.Artist;
 import org.apache.cayenne.unit.di.server.ServerCase;
 import org.apache.cayenne.unit.di.server.UseServerRuntime;
 
-import java.sql.Connection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static org.mockito.Mockito.mock;
-
 /**
- * Test for Result directive to check if we could use ResultDitrective 
optionally.
+ * Test for Result directive to check if we could use ResultDitrective
+ * optionally.
  */
 @UseServerRuntime(ServerCase.TESTMAP_PROJECT)
 public class ResultDirectiveIT extends ServerCase {
 
-    @Inject
-    private ServerRuntime runtime;
-
-    @Inject
-    private DBHelper dbHelper;
-
-    @Inject
-    private JdbcAdapter dbAdapter;
-
-    @Override
-    protected void setUpAfterInjection() throws Exception {
-        dbHelper.deleteAll("PAINTING_INFO");
-        dbHelper.deleteAll("PAINTING");
-        dbHelper.deleteAll("PAINTING1");
-        dbHelper.deleteAll("ARTIST_EXHIBIT");
-        dbHelper.deleteAll("ARTIST_GROUP");
-        dbHelper.deleteAll("ARTIST");
-        dbHelper.deleteAll("EXHIBIT");
-        dbHelper.deleteAll("GALLERY");
-    }
-
-    public void testWithoutResultDirective() throws Exception {
-        String sql = "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST";
-        Map<String, Object> artist = insertArtist();
-        Map<String, Object> selectResult = selectForQuery(sql);
-
-        assertEquals(artist.get("ARTIST_ID"), selectResult.get("ARTIST_ID"));
-        assertEquals(artist.get("ARTIST_NAME"), 
selectResult.get("ARTIST_NAME"));
-    }
-
-    public void testWithOnlyResultDirective() throws Exception {
-        String sql = "SELECT #result('ARTIST_ID' 'java.lang.Integer'),"
-                + " #result('ARTIST_NAME' 'java.lang.String')"
-                + " FROM ARTIST";
-        Map<String, Object> artist = insertArtist();
-        Map<String, Object> selectResult = selectForQuery(sql);
-
-        assertEquals(artist.get("ARTIST_ID"), selectResult.get("ARTIST_ID"));
-        assertEquals(artist.get("ARTIST_NAME"), selectResult
-                .get("ARTIST_NAME")
-                .toString()
-                .trim());
-    }
-
-    public void testWithMixedDirectiveUse1() throws Exception {
-        String sql = "SELECT ARTIST_ID,"
-                + " #result('ARTIST_NAME' 'java.lang.String')"
-                + " FROM ARTIST";
-        Map<String, Object> artist = insertArtist();
-        Map<String, Object> selectResult = selectForQuery(sql);
-
-        assertEquals(artist.get("ARTIST_ID"), selectResult.get("ARTIST_ID"));
-        assertEquals(artist.get("ARTIST_NAME"), selectResult
-                .get("ARTIST_NAME")
-                .toString()
-                .trim());
-    }
-
-    public void testWithMixedDirectiveUse2() throws Exception {
-        String sql = "SELECT #result('ARTIST_ID' 'java.lang.Integer'),"
-                + " ARTIST_NAME "
-                + " FROM ARTIST";
-        Map<String, Object> artist = insertArtist();
-        Map<String, Object> selectResult = selectForQuery(sql);
-
-        assertEquals(artist.get("ARTIST_ID"), selectResult.get("ARTIST_ID"));
-        assertEquals(artist.get("ARTIST_NAME"), 
selectResult.get("ARTIST_NAME"));
-    }
-
-    private Map<String, Object> selectForQuery(String sql) {
-        SQLTemplate template = new SQLTemplate(Artist.class, sql);
-        template.setColumnNamesCapitalization(CapsStrategy.UPPER);
-        MockOperationObserver observer = new MockOperationObserver();
-        runtime.getDataDomain().performQueries(
-                Collections.singletonList(template),
-                observer);
-
-        List<Map<String, Object>> data = observer.rowsForQuery(template);
-        assertEquals(1, data.size());
-        Map<String, Object> row = data.get(0);
-        return row;
-    }
-
-    /**
-     * Inserts one Artist
-     * 
-     * @return Inserted Artist as a DataRow
-     */
-    private Map<String, Object> insertArtist() throws Exception {
-        Map<String, Object> parameters = new HashMap<String, Object>();
-        parameters.put("id", new Integer(1));
-        parameters.put("name", "ArtistToTestResult");
-        String templateString = "INSERT INTO ARTIST (ARTIST_ID, ARTIST_NAME, 
DATE_OF_BIRTH) "
-                + "VALUES (#bind($id), #bind($name), #bind($dob))";
-
-        SQLTemplate template = new SQLTemplate(Object.class, templateString);
-
-        template.setParameters(parameters);
-
-        DataNode node = new DataNode();
-        node.setEntityResolver(runtime.getDataDomain().getEntityResolver());
-        node.setRowReaderFactory(mock(RowReaderFactory.class));
-        node.setAdapter(dbAdapter);
-        
-        SQLTemplateAction action = new SQLTemplateAction(template, node);
-
-        Connection c = runtime
-                .getDataDomain()
-                .getDataNodes()
-                .iterator()
-                .next()
-                .getDataSource()
-                .getConnection();
-        try {
-            MockOperationObserver observer = new MockOperationObserver();
-            action.performAction(c, observer);
-
-            int[] batches = observer.countsForQuery(template);
-            assertNotNull(batches);
-            assertEquals(1, batches.length);
-            assertEquals(1, batches[0]);
-        }
-        finally {
-            c.close();
-        }
-
-        MockOperationObserver observer = new MockOperationObserver();
-        SelectQuery query = new SelectQuery(Artist.class);
-        runtime
-                .getDataDomain()
-                .performQueries(Collections.singletonList(query), observer);
-
-        List<?> data = observer.rowsForQuery(query);
-        assertEquals(1, data.size());
-        DataRow row = (DataRow) data.get(0);
-        return row;
-    }
+       @Inject
+       private ServerRuntime runtime;
+
+       @Inject
+       private DBHelper dbHelper;
+
+       @Inject
+       private JdbcAdapter dbAdapter;
+
+       @Inject
+       private DataNode node;
+
+       @Override
+       protected void setUpAfterInjection() throws Exception {
+               dbHelper.deleteAll("PAINTING_INFO");
+               dbHelper.deleteAll("PAINTING");
+               dbHelper.deleteAll("PAINTING1");
+               dbHelper.deleteAll("ARTIST_EXHIBIT");
+               dbHelper.deleteAll("ARTIST_GROUP");
+               dbHelper.deleteAll("ARTIST");
+               dbHelper.deleteAll("EXHIBIT");
+               dbHelper.deleteAll("GALLERY");
+       }
+
+       public void testWithoutResultDirective() throws Exception {
+               String sql = "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST";
+               Map<String, Object> artist = insertArtist();
+               Map<String, Object> selectResult = selectForQuery(sql);
+
+               assertEquals(artist.get("ARTIST_ID"), 
selectResult.get("ARTIST_ID"));
+               assertEquals(artist.get("ARTIST_NAME"), 
selectResult.get("ARTIST_NAME"));
+       }
+
+       public void testWithOnlyResultDirective() throws Exception {
+               String sql = "SELECT #result('ARTIST_ID' 'java.lang.Integer')," 
+ " #result('ARTIST_NAME' 'java.lang.String')"
+                               + " FROM ARTIST";
+               Map<String, Object> artist = insertArtist();
+               Map<String, Object> selectResult = selectForQuery(sql);
+
+               assertEquals(artist.get("ARTIST_ID"), 
selectResult.get("ARTIST_ID"));
+               assertEquals(artist.get("ARTIST_NAME"), 
selectResult.get("ARTIST_NAME").toString().trim());
+       }
+
+       public void testWithMixedDirectiveUse1() throws Exception {
+               String sql = "SELECT ARTIST_ID," + " #result('ARTIST_NAME' 
'java.lang.String')" + " FROM ARTIST";
+               Map<String, Object> artist = insertArtist();
+               Map<String, Object> selectResult = selectForQuery(sql);
+
+               assertEquals(artist.get("ARTIST_ID"), 
selectResult.get("ARTIST_ID"));
+               assertEquals(artist.get("ARTIST_NAME"), 
selectResult.get("ARTIST_NAME").toString().trim());
+       }
+
+       public void testWithMixedDirectiveUse2() throws Exception {
+               String sql = "SELECT #result('ARTIST_ID' 'java.lang.Integer')," 
+ " ARTIST_NAME " + " FROM ARTIST";
+               Map<String, Object> artist = insertArtist();
+               Map<String, Object> selectResult = selectForQuery(sql);
+
+               assertEquals(artist.get("ARTIST_ID"), 
selectResult.get("ARTIST_ID"));
+               assertEquals(artist.get("ARTIST_NAME"), 
selectResult.get("ARTIST_NAME"));
+       }
+
+       private Map<String, Object> selectForQuery(String sql) {
+               SQLTemplate template = new SQLTemplate(Artist.class, sql);
+               template.setColumnNamesCapitalization(CapsStrategy.UPPER);
+               MockOperationObserver observer = new MockOperationObserver();
+               
runtime.getDataDomain().performQueries(Collections.singletonList(template), 
observer);
+
+               List<Map<String, Object>> data = 
observer.rowsForQuery(template);
+               assertEquals(1, data.size());
+               Map<String, Object> row = data.get(0);
+               return row;
+       }
+
+       /**
+        * Inserts one Artist
+        */
+       private Map<String, Object> insertArtist() throws Exception {
+               Map<String, Object> parameters = new HashMap<String, Object>();
+               parameters.put("id", 1);
+               parameters.put("name", "ArtistToTestResult");
+               String templateString = "INSERT INTO ARTIST (ARTIST_ID, 
ARTIST_NAME, DATE_OF_BIRTH) "
+                               + "VALUES (#bind($id), #bind($name), 
#bind($dob))";
+
+               SQLTemplate template = new SQLTemplate(Object.class, 
templateString);
+
+               template.setParams(parameters);
+
+               SQLTemplateAction action = new SQLTemplateAction(template, 
node);
+
+               Connection c = 
runtime.getDataDomain().getDataNodes().iterator().next().getDataSource().getConnection();
+               try {
+                       MockOperationObserver observer = new 
MockOperationObserver();
+                       action.performAction(c, observer);
+
+                       int[] batches = observer.countsForQuery(template);
+                       assertNotNull(batches);
+                       assertEquals(1, batches.length);
+                       assertEquals(1, batches[0]);
+               } finally {
+                       c.close();
+               }
+
+               MockOperationObserver observer = new MockOperationObserver();
+               SelectQuery query = new SelectQuery(Artist.class);
+               
runtime.getDataDomain().performQueries(Collections.singletonList(query), 
observer);
+
+               List<?> data = observer.rowsForQuery(query);
+               assertEquals(1, data.size());
+               DataRow row = (DataRow) data.get(0);
+               return row;
+       }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorChainTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorChainTest.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorChainTest.java
deleted file mode 100644
index d0e812a..0000000
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorChainTest.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.velocity;
-
-import org.apache.cayenne.access.jdbc.SQLStatement;
-import org.apache.cayenne.velocity.SQLTemplateProcessor;
-import org.junit.Test;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-
-public class SQLTemplateProcessorChainTest {
-
-    @Test
-    public void testProcessTemplateNoChunks() throws Exception {
-        // whatever is inside the chain, it should render as empty if there
-        // is no chunks...
-
-        SQLStatement compiled = new SQLTemplateProcessor().processTemplate(
-                "#chain(' AND ') #end",
-                Collections.EMPTY_MAP);
-
-        assertEquals("", compiled.getSql());
-
-        compiled = new SQLTemplateProcessor().processTemplate(
-                "#chain(' AND ') garbage #end",
-                Collections.EMPTY_MAP);
-
-        assertEquals("", compiled.getSql());
-
-        compiled = new SQLTemplateProcessor().processTemplate(
-                "#chain(' AND ' 'PREFIX') #end",
-                Collections.EMPTY_MAP);
-
-        assertEquals("", compiled.getSql());
-        compiled = new SQLTemplateProcessor().processTemplate(
-                "#chain(' AND ' 'PREFIX') garbage #end",
-                Collections.EMPTY_MAP);
-
-        assertEquals("", compiled.getSql());
-    }
-
-    @Test
-    public void testProcessTemplateFullChain() throws Exception {
-        String template = "#chain(' OR ')"
-                + "#chunk($a)$a#end"
-                + "#chunk($b)$b#end"
-                + "#chunk($c)$c#end"
-                + "#end";
-
-        Map map = new HashMap();
-        map.put("a", "[A]");
-        map.put("b", "[B]");
-        map.put("c", "[C]");
-
-        SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(template, map);
-        assertEquals("[A] OR [B] OR [C]", compiled.getSql());
-    }
-
-    @Test
-    public void testProcessTemplateFullChainAndPrefix() throws Exception {
-        String template = "#chain(' OR ' 'WHERE ')"
-                + "#chunk($a)$a#end"
-                + "#chunk($b)$b#end"
-                + "#chunk($c)$c#end"
-                + "#end";
-
-        Map map = new HashMap();
-        map.put("a", "[A]");
-        map.put("b", "[B]");
-        map.put("c", "[C]");
-
-        SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(template, map);
-        assertEquals("WHERE [A] OR [B] OR [C]", compiled.getSql());
-    }
-
-    @Test
-    public void testProcessTemplatePartialChainMiddle() throws Exception {
-        String template = "#chain(' OR ' 'WHERE ')"
-                + "#chunk($a)$a#end"
-                + "#chunk($b)$b#end"
-                + "#chunk($c)$c#end"
-                + "#end";
-
-        Map map = new HashMap();
-        map.put("a", "[A]");
-        map.put("c", "[C]");
-
-        SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(template, map);
-        assertEquals("WHERE [A] OR [C]", compiled.getSql());
-    }
-
-    @Test
-    public void testProcessTemplatePartialChainStart() throws Exception {
-        String template = "#chain(' OR ' 'WHERE ')"
-                + "#chunk($a)$a#end"
-                + "#chunk($b)$b#end"
-                + "#chunk($c)$c#end"
-                + "#end";
-
-        Map map = new HashMap();
-        map.put("b", "[B]");
-        map.put("c", "[C]");
-
-        SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(template, map);
-        assertEquals("WHERE [B] OR [C]", compiled.getSql());
-    }
-
-    @Test
-    public void testProcessTemplatePartialChainEnd() throws Exception {
-        String template = "#chain(' OR ' 'WHERE ')"
-                + "#chunk($a)$a#end"
-                + "#chunk($b)$b#end"
-                + "#chunk($c)$c#end"
-                + "#end";
-
-        Map map = new HashMap();
-        map.put("a", "[A]");
-        map.put("b", "[B]");
-
-        SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(template, map);
-        assertEquals("WHERE [A] OR [B]", compiled.getSql());
-    }
-
-    @Test
-    public void testProcessTemplateChainWithGarbage() throws Exception {
-        String template = "#chain(' OR ' 'WHERE ')"
-                + "#chunk($a)$a#end"
-                + " some other stuff"
-                + "#chunk($c)$c#end"
-                + "#end";
-
-        Map map = new HashMap();
-        map.put("a", "[A]");
-        map.put("c", "[C]");
-
-        SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(template, map);
-        assertEquals("WHERE [A] some other stuff OR [C]", compiled.getSql());
-    }
-
-    @Test
-    public void testProcessTemplateChainUnconditionalChunks() throws Exception 
{
-        String template = "#chain(' OR ' 'WHERE ')"
-                + "#chunk()C1#end"
-                + "#chunk()C2#end"
-                + "#chunk()C3#end"
-                + "#end";
-
-        SQLStatement compiled = new SQLTemplateProcessor().processTemplate(
-                template,
-                Collections.EMPTY_MAP);
-        assertEquals("WHERE C1 OR C2 OR C3", compiled.getSql());
-    }
-
-    @Test
-    public void testProcessTemplateEmptyChain() throws Exception {
-        String template = "#chain(' OR ' 'WHERE ')"
-                + "#chunk($a)$a#end"
-                + "#chunk($b)$b#end"
-                + "#chunk($c)$c#end"
-                + "#end";
-
-        SQLStatement compiled = new SQLTemplateProcessor().processTemplate(
-                template,
-                Collections.EMPTY_MAP);
-        assertEquals("", compiled.getSql());
-    }
-
-    @Test
-    public void testProcessTemplateWithFalseOrZero1() throws Exception {
-        String template = "#chain(' OR ' 'WHERE ')"
-                + "#chunk($a)[A]#end"
-                + "#chunk($b)[B]#end"
-                + "#chunk($c)$c#end"
-                + "#end";
-
-        Map map = new HashMap();
-        map.put("a", false);
-        map.put("b", 0);
-
-        SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(template, map);
-        assertEquals("WHERE [A] OR [B]", compiled.getSql());
-    }
-
-    @Test
-    public void testProcessTemplateWithFalseOrZero2() throws Exception {
-        String template = "#chain(' OR ' 'WHERE ')"
-                + "#chunk($a)$a#end"
-                + "#chunk($b)$b#end"
-                + "#chunk($c)$c#end"
-                + "#end";
-
-        Map map = new HashMap();
-        map.put("a", false);
-        map.put("b", 0);
-
-        SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(template, map);
-        assertEquals("WHERE false OR 0", compiled.getSql());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorSelectTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorSelectTest.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorSelectTest.java
deleted file mode 100644
index 3481982..0000000
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorSelectTest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.velocity;
-
-import org.apache.cayenne.access.jdbc.ColumnDescriptor;
-import org.apache.cayenne.access.jdbc.SQLStatement;
-import org.apache.cayenne.velocity.SQLTemplateProcessor;
-import org.junit.Test;
-
-import java.util.Collections;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-public class SQLTemplateProcessorSelectTest {
-
-    @Test
-    public void testProcessTemplateUnchanged() throws Exception {
-        String sqlTemplate = "SELECT * FROM ME";
-
-        SQLStatement compiled = new SQLTemplateProcessor().processTemplate(
-                sqlTemplate,
-                Collections.EMPTY_MAP);
-
-        assertEquals(sqlTemplate, compiled.getSql());
-        assertEquals(0, compiled.getBindings().length);
-        assertEquals(0, compiled.getResultColumns().length);
-    }
-
-    @Test
-    public void testProcessSelectTemplate1() throws Exception {
-        String sqlTemplate = "SELECT #result('A') FROM ME";
-
-        SQLStatement compiled = new SQLTemplateProcessor().processTemplate(
-                sqlTemplate,
-                Collections.EMPTY_MAP);
-
-        assertEquals("SELECT A FROM ME", compiled.getSql());
-        assertEquals(0, compiled.getBindings().length);
-        assertEquals(1, compiled.getResultColumns().length);
-        assertEquals("A", compiled.getResultColumns()[0].getName());
-        assertNull(compiled.getResultColumns()[0].getJavaClass());
-    }
-
-    @Test
-    public void testProcessSelectTemplate2() throws Exception {
-        String sqlTemplate = "SELECT #result('A' 'String') FROM ME";
-
-        SQLStatement compiled = new SQLTemplateProcessor().processTemplate(
-                sqlTemplate,
-                Collections.EMPTY_MAP);
-
-        assertEquals("SELECT A FROM ME", compiled.getSql());
-        assertEquals(0, compiled.getBindings().length);
-
-        assertEquals(1, compiled.getResultColumns().length);
-        assertEquals("A", compiled.getResultColumns()[0].getName());
-        assertEquals("java.lang.String", 
compiled.getResultColumns()[0].getJavaClass());
-    }
-
-    @Test
-    public void testProcessSelectTemplate3() throws Exception {
-        String sqlTemplate = "SELECT #result('A' 'String' 'B') FROM ME";
-
-        SQLStatement compiled = new SQLTemplateProcessor().processTemplate(
-                sqlTemplate,
-                Collections.EMPTY_MAP);
-
-        assertEquals("SELECT A AS B FROM ME", compiled.getSql());
-        assertEquals(0, compiled.getBindings().length);
-
-        assertEquals(1, compiled.getResultColumns().length);
-        ColumnDescriptor column = compiled.getResultColumns()[0];
-        assertEquals("A", column.getName());
-        assertEquals("B", column.getDataRowKey());
-        assertEquals("java.lang.String", column.getJavaClass());
-    }
-
-    @Test
-    public void testProcessSelectTemplate4() throws Exception {
-        String sqlTemplate = "SELECT #result('A'), #result('B'), #result('C') 
FROM ME";
-
-        SQLStatement compiled = new SQLTemplateProcessor().processTemplate(
-                sqlTemplate,
-                Collections.EMPTY_MAP);
-
-        assertEquals("SELECT A, B, C FROM ME", compiled.getSql());
-        assertEquals(0, compiled.getBindings().length);
-
-        assertEquals(3, compiled.getResultColumns().length);
-        assertEquals("A", compiled.getResultColumns()[0].getName());
-        assertEquals("B", compiled.getResultColumns()[1].getName());
-        assertEquals("C", compiled.getResultColumns()[2].getName());
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5aedf54e/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorTest.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorTest.java
deleted file mode 100644
index 89f48dc..0000000
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/velocity/SQLTemplateProcessorTest.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.velocity;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.sql.Types;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.cayenne.CayenneDataObject;
-import org.apache.cayenne.DataObject;
-import org.apache.cayenne.ObjectId;
-import org.apache.cayenne.access.jdbc.ParameterBinding;
-import org.apache.cayenne.access.jdbc.SQLStatement;
-import org.junit.Test;
-
-public class SQLTemplateProcessorTest {
-
-       @Test
-       public void testProcessTemplateUnchanged1() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME";
-
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate,
-                               Collections.<String, Object> emptyMap());
-
-               assertEquals(sqlTemplate, compiled.getSql());
-               assertEquals(0, compiled.getBindings().length);
-       }
-
-       @Test
-       public void testProcessTemplateUnchanged2() throws Exception {
-               String sqlTemplate = "SELECT a.b as XYZ FROM $SYSTEM_TABLE";
-
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate,
-                               Collections.<String, Object> emptyMap());
-
-               assertEquals(sqlTemplate, compiled.getSql());
-               assertEquals(0, compiled.getBindings().length);
-       }
-
-       @Test
-       public void testProcessTemplateSimpleDynamicContent() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME WHERE $a";
-
-               Map<String, Object> map = Collections.<String, Object> 
singletonMap("a", "VALUE_OF_A");
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-
-               assertEquals("SELECT * FROM ME WHERE VALUE_OF_A", 
compiled.getSql());
-
-               // bindings are not populated, since no "bind" macro is used.
-               assertEquals(0, compiled.getBindings().length);
-       }
-
-       @Test
-       public void testProcessTemplateBind() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME WHERE "
-                               + "COLUMN1 = #bind($a 'VARCHAR') AND COLUMN2 = 
#bind($b 'INTEGER')";
-               Map<String, Object> map = Collections.<String, Object> 
singletonMap("a", "VALUE_OF_A");
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-
-               assertEquals("SELECT * FROM ME WHERE COLUMN1 = ? AND COLUMN2 = 
?", compiled.getSql());
-               assertEquals(2, compiled.getBindings().length);
-               assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]);
-               assertBindingValue(null, compiled.getBindings()[1]);
-       }
-
-       @Test
-       public void testProcessTemplateBindGuessVarchar() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = 
#bind($a)";
-               Map<String, Object> map = Collections.<String, Object> 
singletonMap("a", "VALUE_OF_A");
-
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-
-               assertEquals(1, compiled.getBindings().length);
-               assertBindingType(Types.VARCHAR, compiled.getBindings()[0]);
-       }
-
-       @Test
-       public void testProcessTemplateBindGuessInteger() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = 
#bind($a)";
-               Map<String, Object> map = Collections.<String, Object> 
singletonMap("a", 4);
-
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-
-               assertEquals(1, compiled.getBindings().length);
-               assertBindingType(Types.INTEGER, compiled.getBindings()[0]);
-       }
-
-       @Test
-       public void testProcessTemplateBindEqual() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME WHERE COLUMN 
#bindEqual($a 'VARCHAR')";
-
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate,
-                               Collections.<String, Object> emptyMap());
-
-               assertEquals("SELECT * FROM ME WHERE COLUMN IS NULL", 
compiled.getSql());
-               assertEquals(0, compiled.getBindings().length);
-
-               Map<String, Object> map = Collections.<String, Object> 
singletonMap("a", "VALUE_OF_A");
-
-               compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-
-               assertEquals("SELECT * FROM ME WHERE COLUMN = ?", 
compiled.getSql());
-               assertEquals(1, compiled.getBindings().length);
-               assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]);
-       }
-
-       @Test
-       public void testProcessTemplateBindNotEqual() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME WHERE COLUMN 
#bindNotEqual($a 'VARCHAR')";
-
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate,
-                               Collections.<String, Object> emptyMap());
-
-               assertEquals("SELECT * FROM ME WHERE COLUMN IS NOT NULL", 
compiled.getSql());
-               assertEquals(0, compiled.getBindings().length);
-
-               Map<String, Object> map = Collections.<String, Object> 
singletonMap("a", "VALUE_OF_A");
-
-               compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-
-               assertEquals("SELECT * FROM ME WHERE COLUMN <> ?", 
compiled.getSql());
-               assertEquals(1, compiled.getBindings().length);
-               assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]);
-       }
-
-       @Test
-       public void testProcessTemplateID() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = 
#bind($helper.cayenneExp($a, 'db:ID_COLUMN'))";
-
-               DataObject dataObject = new CayenneDataObject();
-               dataObject.setObjectId(new ObjectId("T", "ID_COLUMN", 5));
-
-               Map<String, Object> map = Collections.<String, Object> 
singletonMap("a", dataObject);
-
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-
-               assertEquals("SELECT * FROM ME WHERE COLUMN1 = ?", 
compiled.getSql());
-               assertEquals(1, compiled.getBindings().length);
-               assertBindingValue(new Integer(5), compiled.getBindings()[0]);
-       }
-
-       @Test
-       public void testProcessTemplateNotEqualID() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME WHERE "
-                               + "COLUMN1 #bindNotEqual($helper.cayenneExp($a, 
'db:ID_COLUMN1')) "
-                               + "AND COLUMN2 
#bindNotEqual($helper.cayenneExp($a, 'db:ID_COLUMN2'))";
-
-               Map<String, Object> idMap = new HashMap<String, Object>();
-               idMap.put("ID_COLUMN1", new Integer(3));
-               idMap.put("ID_COLUMN2", "aaa");
-               ObjectId id = new ObjectId("T", idMap);
-               DataObject dataObject = new CayenneDataObject();
-               dataObject.setObjectId(id);
-
-               Map<String, Object> map = Collections.<String, Object> 
singletonMap("a", dataObject);
-
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-
-               assertEquals("SELECT * FROM ME WHERE COLUMN1 <> ? AND COLUMN2 
<> ?", compiled.getSql());
-               assertEquals(2, compiled.getBindings().length);
-               assertBindingValue(new Integer(3), compiled.getBindings()[0]);
-               assertBindingValue("aaa", compiled.getBindings()[1]);
-       }
-
-       @Test
-       public void testProcessTemplateConditions() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME #if($a) WHERE COLUMN1 > 
#bind($a)#end";
-
-               Map<String, Object> map = Collections.<String, Object> 
singletonMap("a", "VALUE_OF_A");
-
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-
-               assertEquals("SELECT * FROM ME  WHERE COLUMN1 > ?", 
compiled.getSql());
-               assertEquals(1, compiled.getBindings().length);
-               assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]);
-
-               compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, Collections.<String, 
Object> emptyMap());
-
-               assertEquals("SELECT * FROM ME ", compiled.getSql());
-               assertEquals(0, compiled.getBindings().length);
-       }
-
-       @Test
-       public void testProcessTemplateBindCollection() throws Exception {
-               String sqlTemplate = "SELECT * FROM ME WHERE COLUMN IN 
(#bind($list 'VARCHAR'))";
-
-               Map<String, Object> map = Collections.<String, Object> 
singletonMap("list", Arrays.asList("a", "b", "c"));
-               SQLStatement compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-
-               assertEquals("SELECT * FROM ME WHERE COLUMN IN (?,?,?)", 
compiled.getSql());
-               assertEquals(3, compiled.getBindings().length);
-
-               compiled = new 
SQLTemplateProcessor().processTemplate(sqlTemplate, map);
-               assertBindingValue("a", compiled.getBindings()[0]);
-               assertBindingValue("b", compiled.getBindings()[1]);
-               assertBindingValue("c", compiled.getBindings()[2]);
-       }
-
-       protected void assertBindingValue(Object expectedValue, Object binding) 
{
-               assertTrue("Not a binding!", binding instanceof 
ParameterBinding);
-               assertEquals(expectedValue, ((ParameterBinding) 
binding).getValue());
-       }
-
-       protected void assertBindingType(int expectedType, Object binding) {
-               assertTrue("Not a binding!", binding instanceof 
ParameterBinding);
-               assertEquals(expectedType, ((ParameterBinding) 
binding).getJdbcType());
-       }
-
-       protected void assertBindingPrecision(int expectedPrecision, Object 
binding) {
-               assertTrue("Not a binding!", binding instanceof 
ParameterBinding);
-               assertEquals(expectedPrecision, ((ParameterBinding) 
binding).getScale());
-       }
-}

Reply via email to