You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by eh...@apache.org on 2015/01/09 23:35:30 UTC

svn commit: r1650687 - in /lucene/dev/branches/branch_5x: ./ dev-tools/ dev-tools/idea/solr/contrib/velocity/ solr/ solr/contrib/ solr/contrib/velocity/src/java/org/apache/solr/response/ solr/contrib/velocity/src/resources/ solr/contrib/velocity/src/re...

Author: ehatcher
Date: Fri Jan  9 22:35:28 2015
New Revision: 1650687

URL: http://svn.apache.org/r1650687
Log:
SOLR-1723: VelocityResponseWriter improvements
SOLR-2035: Add a VelocityResponseWriter $resource tool for locale-specific string lookups.
Lots of VrW code cleanup, more and improved test cases.
(merged from r1650685 of trunk)

Added:
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/SolrVelocityLogger.java
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/java/org/apache/solr/response/SolrVelocityLogger.java
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/VM_global_library.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/resources/VM_global_library.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/_macros.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/resources/_macros.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/macros.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/resources/macros.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/macros.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/resources/velocity/macros.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/resources.properties
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/resources/velocity/resources.properties
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/file.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test-files/velocity/file.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity-init.properties
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity-init.properties
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/VM_global_library.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/VM_global_library.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/foreach.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/foreach.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/layout.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/layout.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/locale.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/locale.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/macros.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/macros.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/test_macro_legacy_support.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/test_macro_legacy_support.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/test_macro_overridden.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/test_macro_overridden.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/test_macro_visible.vm
      - copied unchanged from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/velocity/test_macro_visible.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test/velocity/
      - copied from r1650685, lucene/dev/trunk/solr/contrib/velocity/src/test/velocity/
    lucene/dev/branches/branch_5x/solr/server/solr/configsets/data_driven_schema_configs/conf/params.json
      - copied unchanged from r1650685, lucene/dev/trunk/solr/server/solr/configsets/data_driven_schema_configs/conf/params.json
Removed:
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/VM_global_library.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/debug.vm
Modified:
    lucene/dev/branches/branch_5x/   (props changed)
    lucene/dev/branches/branch_5x/dev-tools/   (props changed)
    lucene/dev/branches/branch_5x/dev-tools/idea/solr/contrib/velocity/velocity.iml
    lucene/dev/branches/branch_5x/solr/   (props changed)
    lucene/dev/branches/branch_5x/solr/CHANGES.txt   (contents, props changed)
    lucene/dev/branches/branch_5x/solr/contrib/   (props changed)
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/SolrParamResourceLoader.java
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/VelocityResponseWriter.java
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/browse.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/error.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/footer.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/head.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/hit.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/layout.vm
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/solrconfig.xml
    lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test/org/apache/solr/velocity/VelocityResponseWriterTest.java
    lucene/dev/branches/branch_5x/solr/example/   (props changed)
    lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/db/conf/solrconfig.xml
    lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/mail/conf/solrconfig.xml
    lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/rss/conf/solrconfig.xml
    lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/solr/conf/solrconfig.xml
    lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/tika/conf/solrconfig.xml
    lucene/dev/branches/branch_5x/solr/server/   (props changed)
    lucene/dev/branches/branch_5x/solr/server/solr/configsets/data_driven_schema_configs/conf/solrconfig.xml
    lucene/dev/branches/branch_5x/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml

Modified: lucene/dev/branches/branch_5x/dev-tools/idea/solr/contrib/velocity/velocity.iml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/dev-tools/idea/solr/contrib/velocity/velocity.iml?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/dev-tools/idea/solr/contrib/velocity/velocity.iml (original)
+++ lucene/dev/branches/branch_5x/dev-tools/idea/solr/contrib/velocity/velocity.iml Fri Jan  9 22:35:28 2015
@@ -6,8 +6,10 @@
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src/test" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/velocity" type="java-test-resource" />
       <sourceFolder url="file://$MODULE_DIR$/src/test-files" type="java-test-resource" />
       <sourceFolder url="file://$MODULE_DIR$/src/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/resources" type="java-resource" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />

Modified: lucene/dev/branches/branch_5x/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/CHANGES.txt?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/CHANGES.txt (original)
+++ lucene/dev/branches/branch_5x/solr/CHANGES.txt Fri Jan  9 22:35:28 2015
@@ -242,6 +242,9 @@ New Features
 
 * SOLR-6766: Expose HdfsDirectoryFactory Block Cache statistics via JMX.
   (Mike Drob, Mark Miller)
+
+* SOLR-2035: Add a VelocityResponseWriter $resource tool for locale-specific string lookups.
+  (Erik Hatcher)
   
 
 Bug Fixes
@@ -602,6 +605,8 @@ Other Changes
 * SOLR-6932: All HttpClient ConnectionManagers and SolrJ clients should always be shutdown
   in tests and regular code. (Mark Miller)
 
+* SOLR-1723: VelocityResponseWriter improvements (Erik Hatcher)
+
 ==================  4.10.3 ==================
 
 Bug Fixes

Modified: lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/SolrParamResourceLoader.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/SolrParamResourceLoader.java?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/SolrParamResourceLoader.java (original)
+++ lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/SolrParamResourceLoader.java Fri Jan  9 22:35:28 2015
@@ -31,6 +31,8 @@ import java.util.Iterator;
 import java.util.Map;
 
 public class SolrParamResourceLoader extends ResourceLoader {
+  public static final String TEMPLATE_PARAM_PREFIX = VelocityResponseWriter.TEMPLATE + ".";
+
   private Map<String,String> templates = new HashMap<>();
   public SolrParamResourceLoader(SolrQueryRequest request) {
     super();
@@ -44,8 +46,8 @@ public class SolrParamResourceLoader ext
     while (names.hasNext()) {
       String name = names.next();
       
-      if (name.startsWith("v.template.")) {
-        templates.put(name.substring(11) + ".vm",params.get(name));
+      if (name.startsWith(TEMPLATE_PARAM_PREFIX)) {
+        templates.put(name.substring(TEMPLATE_PARAM_PREFIX.length()) + VelocityResponseWriter.TEMPLATE_EXTENSION,params.get(name));
       }
     }
   }

Modified: lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/VelocityResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/VelocityResponseWriter.java?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/VelocityResponseWriter.java (original)
+++ lucene/dev/branches/branch_5x/solr/contrib/velocity/src/java/org/apache/solr/response/VelocityResponseWriter.java Fri Jan  9 22:35:28 2015
@@ -17,46 +17,181 @@
 
 package org.apache.solr.response;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Locale;
 import java.util.Properties;
+import java.util.ResourceBundle;
 
+import org.apache.hadoop.util.StringUtils;
 import org.apache.solr.client.solrj.SolrResponse;
 import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.client.solrj.response.SolrResponseBase;
+import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.SolrCore;
 import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.util.plugin.SolrCoreAware;
 import org.apache.velocity.Template;
 import org.apache.velocity.VelocityContext;
 import org.apache.velocity.app.VelocityEngine;
 import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
+import org.apache.velocity.tools.ConversionUtils;
 import org.apache.velocity.tools.generic.ComparisonDateTool;
 import org.apache.velocity.tools.generic.DisplayTool;
 import org.apache.velocity.tools.generic.EscapeTool;
 import org.apache.velocity.tools.generic.ListTool;
 import org.apache.velocity.tools.generic.MathTool;
 import org.apache.velocity.tools.generic.NumberTool;
+import org.apache.velocity.tools.generic.ResourceTool;
 import org.apache.velocity.tools.generic.SortTool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public class VelocityResponseWriter implements QueryResponseWriter {
+public class VelocityResponseWriter implements QueryResponseWriter, SolrCoreAware {
+  // init param names, these are _only_ loaded at init time (no per-request control of these)
+  //   - multiple different named writers could be created with different init params
+  public static final String TEMPLATE_BASE_DIR = "template.base.dir";
+  public static final String PARAMS_RESOURCE_LOADER_ENABLED = "params.resource.loader.enabled";
+  public static final String SOLR_RESOURCE_LOADER_ENABLED = "solr.resource.loader.enabled";
+  public static final String PROPERTIES_FILE = "init.properties.file";
+
+  // request param names
+  public static final String TEMPLATE = "v.template";
+  public static final String LAYOUT = "v.layout";
+  public static final String LAYOUT_ENABLED = "v.layout.enabled";
+  public static final String CONTENT_TYPE = "v.contentType";
+  public static final String JSON = "v.json";
+  public static final String LOCALE = "v.locale";
+
+  public static final String TEMPLATE_EXTENSION = ".vm";
+  public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=UTF-8";
+
+  private File fileResourceLoaderBaseDir;
+  private boolean paramsResourceLoaderEnabled;
+  private boolean solrResourceLoaderEnabled;
+  private String initPropertiesFileName;  // used just to hold from init() to inform()
+
+  private static final Logger log = LoggerFactory.getLogger(VelocityResponseWriter.class);
+  private static final SolrVelocityLogger velocityLogger = new SolrVelocityLogger(log);
+  private Properties velocityInitProps = new Properties();
 
-  // TODO: maybe pass this Logger to the template for logging from there?
-//  private static final Logger log = LoggerFactory.getLogger(VelocityResponseWriter.class);
+  @Override
+  public void init(NamedList args) {
+    fileResourceLoaderBaseDir = null;
+    String templateBaseDir = (String) args.get(TEMPLATE_BASE_DIR);
+
+    if (templateBaseDir != null && !templateBaseDir.isEmpty()) {
+      fileResourceLoaderBaseDir = new File(templateBaseDir).getAbsoluteFile();
+      if (!fileResourceLoaderBaseDir.exists()) { // "*not* exists" condition!
+        log.warn(TEMPLATE_BASE_DIR + " specified does not exist: " + fileResourceLoaderBaseDir);
+        fileResourceLoaderBaseDir = null;
+      } else {
+        if (!fileResourceLoaderBaseDir.isDirectory()) { // "*not* a directory" condition
+          log.warn(TEMPLATE_BASE_DIR + " specified is not a directory: " + fileResourceLoaderBaseDir);
+          fileResourceLoaderBaseDir = null;
+        }
+      }
+    }
+
+    // params resource loader: off by default
+    Boolean prle = args.getBooleanArg(PARAMS_RESOURCE_LOADER_ENABLED);
+    paramsResourceLoaderEnabled = (null == prle ? false : prle);
+
+    // solr resource loader: on by default
+    Boolean srle = args.getBooleanArg(SOLR_RESOURCE_LOADER_ENABLED);
+    solrResourceLoaderEnabled = (null == srle ? true : srle);
+
+    initPropertiesFileName = (String) args.get(PROPERTIES_FILE);
+  }
+
+  @Override
+  public void inform(SolrCore core) {
+    // need to leverage SolrResourceLoader, so load init.properties.file here instead of init()
+    if (initPropertiesFileName != null) {
+      InputStream is = null;
+      try {
+        velocityInitProps.load(new InputStreamReader(core.getResourceLoader().openResource(initPropertiesFileName), StandardCharsets.UTF_8));
+      } catch (IOException e) {
+        log.warn("Error loading " + PROPERTIES_FILE + " specified property file: " + initPropertiesFileName, e);
+      }
+    }
+  }
+
+  @Override
+  public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
+    return request.getParams().get(CONTENT_TYPE, DEFAULT_CONTENT_TYPE);
+  }
 
   @Override
   public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException {
-    VelocityEngine engine = getEngine(request);  // TODO: have HTTP headers available for configuring engine
+    VelocityEngine engine = createEngine(request);  // TODO: have HTTP headers available for configuring engine
 
     Template template = getTemplate(engine, request);
 
+    VelocityContext context = createContext(request, response);
+    context.put("engine", engine);  // for $engine.resourceExists(...)
+
+    String layoutTemplate = request.getParams().get(LAYOUT);
+    boolean layoutEnabled = request.getParams().getBool(LAYOUT_ENABLED, true) && layoutTemplate != null;
+
+    String jsonWrapper = request.getParams().get(JSON);
+    boolean wrapResponse = layoutEnabled || jsonWrapper != null;
+
+    // create output
+    if (!wrapResponse) {
+      // straight-forward template/context merge to output
+      template.merge(context, writer);
+    }
+    else {
+      // merge to a string buffer, then wrap with layout and finally as JSON
+      StringWriter stringWriter = new StringWriter();
+      template.merge(context, stringWriter);
+
+      if (layoutEnabled) {
+        context.put("content", stringWriter.toString());
+        stringWriter = new StringWriter();
+        try {
+          engine.getTemplate(layoutTemplate + TEMPLATE_EXTENSION).merge(context, stringWriter);
+        } catch (Exception e) {
+          throw new IOException(e.getMessage());
+        }
+      }
+
+      if (jsonWrapper != null) {
+        writer.write(jsonWrapper + "(");
+        writer.write(getJSONWrap(stringWriter.toString()));
+        writer.write(')');
+      } else {  // using a layout, but not JSON wrapping
+        writer.write(stringWriter.toString());
+      }
+    }
+  }
+
+  private VelocityContext createContext(SolrQueryRequest request, SolrQueryResponse response) {
     VelocityContext context = new VelocityContext();
 
     context.put("request", request);
 
+    // Register useful Velocity "tools"
+    context.put("esc", new EscapeTool());
+    context.put("date", new ComparisonDateTool());
+    context.put("list", new ListTool());
+    context.put("math", new MathTool());
+    context.put("number", new NumberTool());
+    context.put("sort", new SortTool());
+    context.put("display", new DisplayTool());
+    context.put("resource", new SolrVelocityResourceTool(
+        request.getCore().getSolrConfig().getResourceLoader().getClassLoader(),
+        request.getParams().get(LOCALE)));
+
     // Turn the SolrQueryResponse into a SolrResponse.
     // QueryResponse has lots of conveniences suitable for a view
     // Problem is, which SolrResponse class to use?
@@ -65,14 +200,15 @@ public class VelocityResponseWriter impl
     // create a new instance.  But for now the implementation simply
     // uses QueryResponse, and if it chokes in a known way, fall back
     // to bare bones SolrResponseBase.
-    // TODO: Can this writer know what the handler class is?  With echoHandler=true it can get its string name at least
+    // Can this writer know what the handler class is?  With echoHandler=true it can get its string name at least
     SolrResponse rsp = new QueryResponse();
     NamedList<Object> parsedResponse = BinaryResponseWriter.getParsedResponse(request, response);
     try {
       rsp.setResponse(parsedResponse);
 
       // page only injected if QueryResponse works
-      context.put("page", new PageTool(request, response));  // page tool only makes sense for a SearchHandler request... *sigh*
+      context.put("page", new PageTool(request, response));  // page tool only makes sense for a SearchHandler request
+      context.put("debug",((QueryResponse)rsp).getDebugMap());
     } catch (ClassCastException e) {
       // known edge case where QueryResponse's extraction assumes "response" is a SolrDocumentList
       // (AnalysisRequestHandler emits a "response")
@@ -81,81 +217,69 @@ public class VelocityResponseWriter impl
     }
     context.put("response", rsp);
 
-    // Velocity context tools - TODO: make these pluggable
-    context.put("esc", new EscapeTool());
-    context.put("date", new ComparisonDateTool());
-    context.put("list", new ListTool());
-    context.put("math", new MathTool());
-    context.put("number", new NumberTool());
-    context.put("sort", new SortTool());
-    context.put("display", new DisplayTool());
-
-    context.put("engine", engine);  // for $engine.resourceExists(...)
-
-    String layout_template = request.getParams().get("v.layout");
-    String json_wrapper = request.getParams().get("v.json");
-    boolean wrap_response = (layout_template != null) || (json_wrapper != null);
-
-    // create output, optionally wrap it into a json object
-    if (wrap_response) {
-      StringWriter stringWriter = new StringWriter();
-      template.merge(context, stringWriter);
-
-      if (layout_template != null) {
-        context.put("content", stringWriter.toString());
-        stringWriter = new StringWriter();
-        try {
-          engine.getTemplate(layout_template + ".vm").merge(context, stringWriter);
-        } catch (Exception e) {
-          throw new IOException(e.getMessage());
-        }
-      }
-
-      if (json_wrapper != null) {
-        writer.write(request.getParams().get("v.json") + "(");
-        writer.write(getJSONWrap(stringWriter.toString()));
-        writer.write(')');
-      } else {  // using a layout, but not JSON wrapping
-        writer.write(stringWriter.toString());
-      }
-    } else {
-      template.merge(context, writer);
-    }
+    return context;
   }
 
-  private VelocityEngine getEngine(SolrQueryRequest request) {
+  private VelocityEngine createEngine(SolrQueryRequest request) {
     VelocityEngine engine = new VelocityEngine();
 
-    engine.setProperty("params.resource.loader.instance", new SolrParamResourceLoader(request));
-    SolrVelocityResourceLoader resourceLoader =
-        new SolrVelocityResourceLoader(request.getCore().getSolrConfig().getResourceLoader());
-    engine.setProperty("solr.resource.loader.instance", resourceLoader);
-    engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "params,solr");
+    // route all Velocity logging through Solr's logging facility
+    engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, velocityLogger);
 
-    // TODO: Externalize Velocity properties
-    String propFile = request.getParams().get("v.properties");
-    try {
-      Properties props = new Properties();
-      // Don't create a separate velocity log file by default.
-      props.put(RuntimeConstants.RUNTIME_LOG, "");
-
-      if (propFile == null) {
-        engine.init(props);
-      } else {
-        InputStream is = null;
-        try {
-          is = resourceLoader.getResourceStream(propFile);
-          props.load(new InputStreamReader(is, StandardCharsets.UTF_8));
-          engine.init(props);
-        }
-        finally {
-          if (is != null) is.close();
-        }
-      }
+    // Set some engine properties that improve the experience
+    //   - these could be considered in the future for parameterization, but can also be overridden by using
+    //     the init.properties.file setting.  (TODO: add a test for this)
+
+    // load the built-in _macros.vm first, then load VM_global_library.vm for legacy (pre-5.0) support,
+    // and finally allow macros.vm to have the final say and override anything defined in the preceding files.
+    engine.setProperty(RuntimeConstants.VM_LIBRARY, "_macros.vm,VM_global_library.vm,macros.vm");
+
+    // Standard templates autoload, but not the macro one(s), by default, so let's just make life
+    // easier, and consistent, for macro development too.
+    engine.setProperty(RuntimeConstants.VM_LIBRARY_AUTORELOAD, "true");
+
+    /*
+      Set up Velocity resource loader(s)
+       terminology note: "resource loader" is overloaded here, there is Solr's resource loader facility for plugins,
+       and there are Velocity template resource loaders.  It's confusing, they overlap: there is a Velocity resource
+       loader that loads templates from Solr's resource loader (SolrVelocityResourceLoader).
+
+      The Velocity resource loader order is [params,][file,][solr], intentionally ordered in this manner, and each
+      one optional and individually enable-able.  By default, only "solr" (resource loader) is used, parsing templates
+      from a velocity/ sub-tree in either the classpath or under conf/.
+
+      A common usage would be to enable the file template loader, keeping the solr loader enabled; the Velocity resource
+      loader path would then be "file,solr" (params is disabled by default).  The basic browse templates are built into
+      this plugin, but can be individually overridden by placing a same-named template in the template.base.dir specified
+      directory.
+     */
+    ArrayList<String> loaders = new ArrayList<String>();
+    if (paramsResourceLoaderEnabled) {
+      loaders.add("params");
+      engine.setProperty("params.resource.loader.instance", new SolrParamResourceLoader(request));
     }
-    catch (Exception e) {
-      throw new RuntimeException(e);
+    if (fileResourceLoaderBaseDir != null) {
+      loaders.add("file");
+      engine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, fileResourceLoaderBaseDir.getAbsolutePath());
     }
+    if (solrResourceLoaderEnabled) {
+      // The solr resource loader serves templates under a velocity/ subtree from <lib>, conf/,
+      // or SolrCloud's configuration tree.  Or rather the other way around, other resource loaders are rooted
+      // from the top, whereas this is velocity/ sub-tree rooted.
+      loaders.add("solr");
+      engine.setProperty("solr.resource.loader.instance", new SolrVelocityResourceLoader(request.getCore().getSolrConfig().getResourceLoader()));
+    }
+
+    // Always have the built-in classpath loader.  This is needed when using VM_LIBRARY macros, as they are required
+    // to be present if specified, and we want to have a nice macros facility built-in for users to use easily, and to
+    // extend in custom ways.
+    loaders.add("builtin");
+    engine.setProperty("builtin.resource.loader.instance", new ClasspathResourceLoader());
+
+    engine.setProperty(RuntimeConstants.RESOURCE_LOADER, StringUtils.join(",", loaders));
+
+    // bring in any custom properties too
+    engine.init(velocityInitProps);
 
     return engine;
   }
@@ -163,18 +287,19 @@ public class VelocityResponseWriter impl
   private Template getTemplate(VelocityEngine engine, SolrQueryRequest request) throws IOException {
     Template template;
 
-    String template_name = request.getParams().get("v.template");
-    String qt = request.getParams().get("qt");
+    String templateName = request.getParams().get(TEMPLATE);
+
+    String qt = request.getParams().get(CommonParams.QT);
     String path = (String) request.getContext().get("path");
-    if (template_name == null && path != null) {
-      template_name = path;
+    if (templateName == null && path != null) {
+      templateName = path;
     }  // TODO: path is never null, so qt won't get picked up  maybe special case for '/select' to use qt, otherwise use path?
-    if (template_name == null && qt != null) {
-      template_name = qt;
+    if (templateName == null && qt != null) {
+      templateName = qt;
     }
-    if (template_name == null) template_name = "index";
+    if (templateName == null) templateName = "index";
     try {
-      template = engine.getTemplate(template_name + ".vm");
+      template = engine.getTemplate(templateName + TEMPLATE_EXTENSION);
     } catch (Exception e) {
       throw new IOException(e.getMessage());
     }
@@ -182,12 +307,7 @@ public class VelocityResponseWriter impl
     return template;
   }
 
-  @Override
-  public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
-    return request.getParams().get("v.contentType", "text/html;charset=UTF-8");
-  }
-
-  private String getJSONWrap(String xmlResult) {  // TODO: maybe noggit or Solr's JSON utilities can make this cleaner?
+  private String getJSONWrap(String xmlResult) {  // maybe noggit or Solr's JSON utilities can make this cleaner?
     // escape the double quotes and backslashes
     String replace1 = xmlResult.replaceAll("\\\\", "\\\\\\\\");
     replace1 = replace1.replaceAll("\\n", "\\\\n");
@@ -197,7 +317,34 @@ public class VelocityResponseWriter impl
     return "{\"result\":\"" + replaced + "\"}";
   }
 
-  @Override
-  public void init(NamedList args) {
+  // see: http://svn.apache.org/repos/asf/velocity/tools/branches/2.0.x/src/main/java/org/apache/velocity/tools/generic/ResourceTool.java
+  private class SolrVelocityResourceTool extends ResourceTool {
+
+    private final Locale locale;
+    private ClassLoader solrClassLoader;
+
+    public SolrVelocityResourceTool(ClassLoader cl, String localeString) {
+      this.solrClassLoader = cl;
+      Locale l = toLocale(localeString);
+      this.locale = (l == null ? Locale.ROOT : l);
+    }
+
+    @Override
+    protected ResourceBundle getBundle(String baseName, Object loc) {
+      // resource bundles for this tool must be in velocity "package"
+      return ResourceBundle.getBundle("velocity." + baseName, locale, solrClassLoader);
+    }
+
+    // Why did Velocity Tools make this private?  Copied from ResourceTools.java
+    private Locale toLocale(Object obj) {
+      if (obj == null) {
+        return null;
+      }
+      if (obj instanceof Locale) {
+        return (Locale) obj;
+      }
+      String s = String.valueOf(obj);
+      return ConversionUtils.toLocale(s);
+    }
   }
 }

Modified: lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/browse.vm
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/browse.vm?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/browse.vm (original)
+++ lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/browse.vm Fri Jan  9 22:35:28 2015
@@ -1,5 +1,3 @@
-#set($params = $request.params)
-
 ## Show Error Message, if any
 <div class="error">
   #parse("error.vm")
@@ -7,11 +5,11 @@
 
 <div class="query-box">
   <form id="query-form" action="#{url_for_home}" method="GET">
-    Find:
-    <input type="text" id="q" name="q" value="$!esc.html($params.get('q'))"/>
+    $resource.find:
+    <input type="text" id="q" name="q" value="$!esc.html($request.params.get('q'))"/>
     <input type="submit"/>
 
-    #if($request.params.get('debugQuery'))
+    #if($debug) ## TODO: this would automatically happen when arbitrary parameters are kept on URLs
       <input type="hidden" name="debug" value="true"/>
     #end
     #foreach($fq in $request.params.getParams('fq'))
@@ -19,7 +17,7 @@
     #end
 
     <div class="constraints">
-      #foreach($fq in $params.getParams('fq'))
+      #foreach($fq in $request.params.getParams('fq'))
         #set($previous_fq_count=$velocityCount - 1)
         #if($fq != '')
           &gt;
@@ -29,7 +27,7 @@
     </div>
 
     <div class="parsed_query_header">
-      #if($request.params.get('debugQuery'))
+      #if($debug)
         <a href="#" onclick='jQuery(this).siblings("div").toggle(); return false;'>toggle parsed query</a>
         <div class="parsed_query" style="display:none">$response.response.debug.parsedquery</div>
       #end
@@ -46,11 +44,10 @@
   <span>
     <span class="results-found">$page.results_found</span>
     results found in
-    ${response.responseHeader.QTime} ms
+    ${response.responseHeader.QTime}ms
   </span>
 
-  Page <span class="page-num">$page.current_page_number</span>
-    of <span class="page-count">$page.page_count</span>
+  $resource.page_of.insert($page.current_page_number,$page.page_count)
 </div>
 
 ## Render Results, actual matching docs
@@ -59,13 +56,12 @@
 </div>
 
 <div class="pagination">
-  #link_to_previous_page("previous")
+  #link_to_previous_page
 
   <span class="results-found">$page.results_found</span>
   results found.
 
-  Page <span class="page-num">$page.current_page_number</span>
-    of <span class="page-count">$page.page_count</span>
+  $resource.page_of.insert($page.current_page_number,$page.page_count)
 
-  #link_to_next_page("next")
+  #link_to_next_page
 </div>

Modified: lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/error.vm
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/error.vm?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/error.vm (original)
+++ lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/error.vm Fri Jan  9 22:35:28 2015
@@ -1,10 +1,3 @@
-#**
- *  Show Error Message, if any
- *#
-
-## Show Error Message, if any
-## Usually rendered inside div class=error
-
 #if( $response.response.error.code )
   <h1>ERROR $response.response.error.code</h1>
   $response.response.error.msg

Modified: lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/footer.vm
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/footer.vm?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/footer.vm (original)
+++ lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/footer.vm Fri Jan  9 22:35:28 2015
@@ -2,14 +2,14 @@
 <div>
   <span>Options:</span>
 
-  #if($request.params.get('debugQuery'))
+  #if($debug)
     <a href="#url_for_home?#q#if($list.size($request.params.getParams('fq')) > 0)&#fqs($request.params.getParams('fq'))#end">
       disable debug</a>
   #else
     <a href="#url_for_lens&debug=true&fl=*,score">enable debug</a>
   #end
   -
-  <a href="#url_for_lens&wt=xml#if($request.params.get('debugQuery'))&debug=true#end">XML results</a>
+  <a href="#url_for_lens&wt=xml#if($debug)&debug=true#end">XML results</a> ## TODO: Add links for other formats, maybe dynamically?
 
 </div>
 

Modified: lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/head.vm
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/head.vm?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/head.vm (original)
+++ lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/head.vm Fri Jan  9 22:35:28 2015
@@ -3,9 +3,12 @@
  *#
 
   <title>Solr browse: #core_name</title>
+
   <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
+
   <link rel="icon" type="image/x-icon" href="#{url_root}/img/favicon.ico"/>
   <link rel="shortcut icon" type="image/x-icon" href="#{url_root}/img/favicon.ico"/>
+
   <script type="text/javascript" src="#{url_root}/js/lib/jquery-1.7.2.min.js"></script>
 
   <style>
@@ -17,17 +20,6 @@
     #head{
       width: 100%;
     }
-    .array-field {
-      border: 2px solid #474747;
-      background: #FFE9D8;
-      padding: 5px;
-      margin: 5px;
-    }
-
-    .array-field-list li {
-      list-style: circle;
-      margin-left: 20px;
-    }
 
     .parsed_query_header {
       font-family: Helvetica, Arial, sans-serif;
@@ -47,7 +39,11 @@
     }
 
     a {
-      color: #43a4b1;
+      color: #305CB3;
+    }
+
+    em {
+      color: #FF833D;
     }
 
     .facets {
@@ -61,7 +57,7 @@
     }
 
     .facets h2 {
-      background: #EA897E;
+      background: #D9411E;
       padding: 2px 5px;
     }
 
@@ -82,18 +78,9 @@
       font-weight: bold;
     }
 
-    .highlight {
-      color: white;
-      background-color: gray;
-      border: 1px black solid;
-    }
-
-    .highlight-box {
-      margin-left: 15px;
-    }
-
     .field-name {
       font-weight: bold;
+      // align="right" valign="top"
     }
 
     .highlighted-facet-field {

Modified: lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/hit.vm
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/hit.vm?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/hit.vm (original)
+++ lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/hit.vm Fri Jan  9 22:35:28 2015
@@ -1,24 +1,27 @@
-#set($docId = $doc.getFieldValue('id'))
+#set($docId = $doc.getFirstValue($request.schema.uniqueKeyField.name))
+
 
 <div class="result-document">
 
   <table>
     #foreach( $fieldName in $doc.fieldNames )
-      #foreach( $value in $doc.getFieldValues($fieldName) )
         <tr>
-          <th align="right" valign="top">
-            #if( $foreach.count == 1 )
-              $fieldName:
-            #end
+          <th align="right" valign="top" style="field-name">
+            $esc.html($fieldName):
           </th>
 
           <td align="left" valign="top">
-            $esc.html($value) <br/>
+            #field($fieldName)
           </td>
         </tr>
-      #end
     #end
   </table>
 
+  #if($debug)
+    <a href="#" onclick='jQuery(this).siblings("pre").toggle(); return false;'>toggle explain</a>
 
+    <pre style="display:none">
+      $response.getExplainMap().get($docId)
+    </pre>
+  #end
 </div>

Modified: lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/layout.vm
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/layout.vm?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/layout.vm (original)
+++ lucene/dev/branches/branch_5x/solr/contrib/velocity/src/resources/velocity/layout.vm Fri Jan  9 22:35:28 2015
@@ -5,7 +5,7 @@
   <body>
     <div id="admin"><a href="#url_root/#/#core_name">Solr Admin</a></div>
     <div id="head">
-      <a href="#url_for_home#if($request.params.get('debugQuery'))?debugQuery=true#end"><img src="#{url_root}/img/solr.svg" id="logo"/></a>
+      <a href="#url_for_home#if($debug)?debug=true#end"><img src="#{url_root}/img/solr.svg" id="logo"/></a>
     </div>
 
     <div id="content">

Modified: lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/solrconfig.xml?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/solrconfig.xml (original)
+++ lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test-files/velocity/solr/collection1/conf/solrconfig.xml Fri Jan  9 22:35:28 2015
@@ -16,65 +16,12 @@
  limitations under the License.
 -->
 
-<!-- 
-     For more details about configurations options that may appear in
-     this file, see http://wiki.apache.org/solr/SolrConfigXml. 
--->
 <config>
   <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
-  <indexConfig>
-    <useCompoundFile>${useCompoundFile:false}</useCompoundFile>
-  </indexConfig>
-
-  <lib dir="../../contrib/velocity/lib" />
-  <lib dir="../../dist/" regex="solr-velocity-\d.*\.jar" />
-  <dataDir>${solr.data.dir:}</dataDir>
-
-
-  <directoryFactory name="DirectoryFactory"
-                    class="${solr.directoryFactory:solr.StandardDirectoryFactory}"/>
-
-  <updateHandler class="solr.DirectUpdateHandler2">
-  </updateHandler>
-  
-  <query>
-    <maxBooleanClauses>1024</maxBooleanClauses>
-    <filterCache class="solr.FastLRUCache"
-                 size="512"
-                 initialSize="512"
-                 autowarmCount="0"/>
-    <queryResultCache class="solr.LRUCache"
-                     size="512"
-                     initialSize="512"
-                     autowarmCount="0"/>
-    <documentCache class="solr.LRUCache"
-                   size="512"
-                   initialSize="512"
-                   autowarmCount="0"/>
-    <enableLazyFieldLoading>true</enableLazyFieldLoading>
-
-   <queryResultWindowSize>20</queryResultWindowSize>
-   <queryResultMaxDocsCached>200</queryResultMaxDocsCached>
-    <listener event="newSearcher" class="solr.QuerySenderListener">
-      <arr name="queries">
-      </arr>
-    </listener>
-    <listener event="firstSearcher" class="solr.QuerySenderListener">
-      <arr name="queries">
-        <lst>
-          <str name="q">static firstSearcher warming in solrconfig.xml</str>
-        </lst>
-      </arr>
-    </listener>
-    <useColdSearcher>false</useColdSearcher>
-    <maxWarmingSearchers>2</maxWarmingSearchers>
-
-  </query>
-  <requestDispatcher handleSelect="true" >
-    <requestParsers enableRemoteStreaming="true"
-                    multipartUploadLimitInKB="2048000" />
-    <httpCaching never304="true" />
-  </requestDispatcher>
+
+  <!--<lib dir="../../contrib/velocity/lib" />-->
+  <!--<lib dir="../../dist/" regex="solr-velocity-\d.*\.jar" />-->
+
 
   <requestHandler name="search" class="solr.SearchHandler" default="true">
      <lst name="defaults">
@@ -83,13 +30,9 @@
      </lst>
     </requestHandler>
 
-  <requestHandler name="/update" class="solr.UpdateRequestHandler"  />
-
   <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter"/>
 
-  <!-- Legacy config for the admin interface -->
-  <admin>
-    <defaultQuery>*:*</defaultQuery>
-  </admin>
-
+  <queryResponseWriter name="velocityWithInitProps" class="solr.VelocityResponseWriter">
+    <str name="init.properties.file">velocity-init.properties</str>
+  </queryResponseWriter>
 </config>

Modified: lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test/org/apache/solr/velocity/VelocityResponseWriterTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test/org/apache/solr/velocity/VelocityResponseWriterTest.java?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test/org/apache/solr/velocity/VelocityResponseWriterTest.java (original)
+++ lucene/dev/branches/branch_5x/solr/contrib/velocity/src/test/org/apache/solr/velocity/VelocityResponseWriterTest.java Fri Jan  9 22:35:28 2015
@@ -18,27 +18,39 @@
 package org.apache.solr.velocity;
 
 import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.util.NamedList;
 import org.apache.solr.response.QueryResponseWriter;
+import org.apache.solr.response.SolrParamResourceLoader;
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.response.VelocityResponseWriter;
 import org.apache.solr.request.SolrQueryRequest;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.io.IOException;
 import java.io.StringWriter;
 
 public class VelocityResponseWriterTest extends SolrTestCaseJ4 {
   @BeforeClass
   public static void beforeClass() throws Exception {
     initCore("solrconfig.xml", "schema.xml", getFile("velocity/solr").getAbsolutePath());
+    System.out.println(getFile("velocity/solr").getAbsolutePath());
   }
 
   @Test
-  public void testCustomParamTemplate() throws Exception {
-    // This test doesn't use the Solr core, just the response writer directly
+  public void testVelocityResponseWriterRegistered() {
+    QueryResponseWriter writer = h.getCore().getQueryResponseWriter("velocity");
+    assertTrue("VrW registered check", writer instanceof VelocityResponseWriter);
+  }
 
+  @Test
+  public void testCustomParamTemplate() throws Exception {
     org.apache.solr.response.VelocityResponseWriter vrw = new VelocityResponseWriter();
-    SolrQueryRequest req = req("v.template","custom", "v.template.custom","$response.response.response_data");
+    NamedList<String> nl = new NamedList<String>();
+    nl.add(VelocityResponseWriter.PARAMS_RESOURCE_LOADER_ENABLED, "true");
+    vrw.init(nl);
+    SolrQueryRequest req = req(VelocityResponseWriter.TEMPLATE,"custom",
+        SolrParamResourceLoader.TEMPLATE_PARAM_PREFIX+"custom","$response.response.response_data");
     SolrQueryResponse rsp = new SolrQueryResponse();
     StringWriter buf = new StringWriter();
     rsp.add("response_data", "testing");
@@ -47,14 +59,92 @@ public class VelocityResponseWriterTest
   }
 
   @Test
-  public void testVelocityResponseWriterRegistered() {
-    QueryResponseWriter writer = h.getCore().getQueryResponseWriter("velocity");
+  public void testParamResourceLoaderDisabled() throws Exception {
+    org.apache.solr.response.VelocityResponseWriter vrw = new VelocityResponseWriter();
+    // by default param resource loader is disabled, no need to set it here
+    SolrQueryRequest req = req(VelocityResponseWriter.TEMPLATE,"custom",
+        SolrParamResourceLoader.TEMPLATE_PARAM_PREFIX+"custom","$response.response.response_data");
+    SolrQueryResponse rsp = new SolrQueryResponse();
+    StringWriter buf = new StringWriter();
+    try {
+      vrw.write(buf, req, rsp);
+      fail("Should have thrown exception due to missing template");
+    } catch (IOException e) {
+      // expected exception
+    }
+  }
 
-    assertTrue("VrW registered check", writer instanceof VelocityResponseWriter);
+  @Test
+  public void testFileResourceLoader() throws Exception {
+    org.apache.solr.response.VelocityResponseWriter vrw = new VelocityResponseWriter();
+    NamedList<String> nl = new NamedList<String>();
+    nl.add("template.base.dir", getFile("velocity").getAbsolutePath());
+    vrw.init(nl);
+    SolrQueryRequest req = req(VelocityResponseWriter.TEMPLATE,"file");
+    SolrQueryResponse rsp = new SolrQueryResponse();
+    StringWriter buf = new StringWriter();
+    vrw.write(buf, req, rsp);
+    assertEquals("testing", buf.toString());
   }
 
   @Test
   public void testSolrResourceLoaderTemplate() throws Exception {
-    assertEquals("0", h.query(req("q","*:*", "wt","velocity","v.template","numFound")));
+    assertEquals("0", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"numFound")));
+  }
+
+  @Test
+  public void testMacros() throws Exception {
+    // tests that a macro in a custom macros.vm is visible
+    assertEquals("test_macro_SUCCESS", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"test_macro_visible")));
+
+    // tests that a builtin (_macros.vm) macro, #url_root in this case, can be overridden in a custom macros.vm
+    // the macro is also defined in VM_global_library.vm, which should also be overridden by macros.vm
+    assertEquals("Loaded from: macros.vm", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"test_macro_overridden")));
+
+    // tests that macros defined in VM_global_library.vm are visible.  This file was where macros in pre-5.0 versions were defined
+    assertEquals("legacy_macro_SUCCESS", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"test_macro_legacy_support")));
   }
+
+  @Test
+  public void testInitProps() throws Exception {
+    // The test init properties file turns off being able to use $foreach.index (the implicit loop counter)
+    // The foreach.vm template uses $!foreach.index, with ! suppressing the literal "$foreach.index" output
+
+    assertEquals("01", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"foreach")));
+    assertEquals("", h.query(req("q","*:*", "wt","velocityWithInitProps",VelocityResponseWriter.TEMPLATE,"foreach")));
+  }
+
+  @Test
+  public void testLocaleFeature() throws Exception {
+    assertEquals("Color", h.query(req("q", "*:*", "wt", "velocity", VelocityResponseWriter.TEMPLATE, "locale",
+        VelocityResponseWriter.LOCALE,"en_US")));
+    assertEquals("Colour", h.query(req("q", "*:*", "wt", "velocity", VelocityResponseWriter.TEMPLATE, "locale",
+        VelocityResponseWriter.LOCALE,"en_UK")));
+  }
+
+  @Test
+  public void testLayoutFeature() throws Exception {
+    assertEquals("{{{0}}}", h.query(req("q","*:*", "wt","velocity",
+        VelocityResponseWriter.TEMPLATE,"numFound", VelocityResponseWriter.LAYOUT,"layout")));
+
+    // even with v.layout specified, layout can be disabled explicitly
+    assertEquals("0", h.query(req("q","*:*", "wt","velocity",
+        VelocityResponseWriter.TEMPLATE,"numFound",
+        VelocityResponseWriter.LAYOUT,"layout",
+        VelocityResponseWriter.LAYOUT_ENABLED,"false")));
+  }
+
+  @Test
+  public void testJSONWrapper() throws Exception {
+    assertEquals("foo({\"result\":\"0\"})", h.query(req("q", "*:*", "wt", "velocity",
+        VelocityResponseWriter.TEMPLATE, "numFound",
+        VelocityResponseWriter.JSON,"foo")));
+
+    // Now with layout, for good measure
+    assertEquals("foo({\"result\":\"{{{0}}}\"})", h.query(req("q", "*:*", "wt", "velocity",
+        VelocityResponseWriter.TEMPLATE, "numFound",
+        VelocityResponseWriter.JSON,"foo",
+        VelocityResponseWriter.LAYOUT,"layout")));
+  }
+
 }

Modified: lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/db/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/db/conf/solrconfig.xml?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/db/conf/solrconfig.xml (original)
+++ lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/db/conf/solrconfig.xml Fri Jan  9 22:35:28 2015
@@ -1727,8 +1727,9 @@
   <!--
      Custom response writers can be declared as needed...
     -->
-    <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
-  
+  <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
+    <str name="template.base.dir">${velocity.template.base.dir:}</str>
+  </queryResponseWriter>
 
   <!-- XSLT response writer transforms the XML output by any xslt file found
        in Solr's conf/xslt directory.  Changes to xslt files are checked for

Modified: lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/mail/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/mail/conf/solrconfig.xml?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/mail/conf/solrconfig.xml (original)
+++ lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/mail/conf/solrconfig.xml Fri Jan  9 22:35:28 2015
@@ -1698,8 +1698,9 @@
   <!--
      Custom response writers can be declared as needed...
     -->
-    <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
-  
+  <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
+    <str name="template.base.dir">${velocity.template.base.dir:}</str>
+  </queryResponseWriter>
 
   <!-- XSLT response writer transforms the XML output by any xslt file found
        in Solr's conf/xslt directory.  Changes to xslt files are checked for

Modified: lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/rss/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/rss/conf/solrconfig.xml?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/rss/conf/solrconfig.xml (original)
+++ lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/rss/conf/solrconfig.xml Fri Jan  9 22:35:28 2015
@@ -1695,8 +1695,9 @@
   <!--
      Custom response writers can be declared as needed...
     -->
-    <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
-  
+  <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
+    <str name="template.base.dir">${velocity.template.base.dir:}</str>
+  </queryResponseWriter>
 
   <!-- XSLT response writer transforms the XML output by any xslt file found
        in Solr's conf/xslt directory.  Changes to xslt files are checked for

Modified: lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/solr/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/solr/conf/solrconfig.xml?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/solr/conf/solrconfig.xml (original)
+++ lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/solr/conf/solrconfig.xml Fri Jan  9 22:35:28 2015
@@ -1726,8 +1726,9 @@
   <!--
      Custom response writers can be declared as needed...
     -->
-    <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
-  
+  <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
+    <str name="template.base.dir">${velocity.template.base.dir:}</str>
+  </queryResponseWriter>
 
   <!-- XSLT response writer transforms the XML output by any xslt file found
        in Solr's conf/xslt directory.  Changes to xslt files are checked for

Modified: lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/tika/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/tika/conf/solrconfig.xml?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/tika/conf/solrconfig.xml (original)
+++ lucene/dev/branches/branch_5x/solr/example/example-DIH/solr/tika/conf/solrconfig.xml Fri Jan  9 22:35:28 2015
@@ -1704,8 +1704,9 @@
   <!--
      Custom response writers can be declared as needed...
     -->
-    <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
-  
+  <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
+    <str name="template.base.dir">${velocity.template.base.dir:}</str>
+  </queryResponseWriter>
 
   <!-- XSLT response writer transforms the XML output by any xslt file found
        in Solr's conf/xslt directory.  Changes to xslt files are checked for

Modified: lucene/dev/branches/branch_5x/solr/server/solr/configsets/data_driven_schema_configs/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/server/solr/configsets/data_driven_schema_configs/conf/solrconfig.xml?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/server/solr/configsets/data_driven_schema_configs/conf/solrconfig.xml (original)
+++ lucene/dev/branches/branch_5x/solr/server/solr/configsets/data_driven_schema_configs/conf/solrconfig.xml Fri Jan  9 22:35:28 2015
@@ -852,22 +852,9 @@
   </requestHandler>
 
 
-  <requestHandler name="/browse" class="solr.SearchHandler">
+  <requestHandler name="/browse" class="solr.SearchHandler" useParams="query,facets,velocity,browse">
     <lst name="defaults">
       <str name="echoParams">explicit</str>
-
-      <!-- VelocityResponseWriter settings -->
-      <str name="wt">velocity</str>
-      <str name="v.template">browse</str>
-      <str name="v.layout">layout</str>
-
-      <!-- Query settings -->
-      <str name="defType">edismax</str>
-      <str name="q.alt">*:*</str>
-
-      <!-- Facet settings -->
-      <str name="facet">on</str>
-
     </lst>
   </requestHandler>
 
@@ -1485,8 +1472,9 @@
   <!--
      Custom response writers can be declared as needed...
     -->
-  <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
-
+  <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
+    <str name="template.base.dir">${velocity.template.base.dir:}</str>
+  </queryResponseWriter>
 
   <!-- XSLT response writer transforms the XML output by any xslt file found
        in Solr's conf/xslt directory.  Changes to xslt files are checked for

Modified: lucene/dev/branches/branch_5x/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml?rev=1650687&r1=1650686&r2=1650687&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml (original)
+++ lucene/dev/branches/branch_5x/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml Fri Jan  9 22:35:28 2015
@@ -1762,7 +1762,9 @@
   <!--
      Custom response writers can be declared as needed...
     -->
-    <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
+    <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
+      <str name="template.base.dir">${velocity.template.base.dir:}</str>
+    </queryResponseWriter>
   
 
   <!-- XSLT response writer transforms the XML output by any xslt file found