Return-Path: X-Original-To: apmail-lucene-commits-archive@www.apache.org Delivered-To: apmail-lucene-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id DAD92175A5 for ; Fri, 9 Jan 2015 22:35:29 +0000 (UTC) Received: (qmail 42608 invoked by uid 500); 9 Jan 2015 22:35:31 -0000 Mailing-List: contact commits-help@lucene.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@lucene.apache.org Delivered-To: mailing list commits@lucene.apache.org Received: (qmail 42598 invoked by uid 99); 9 Jan 2015 22:35:31 -0000 Received: from eris.apache.org (HELO hades.apache.org) (140.211.11.105) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 09 Jan 2015 22:35:31 +0000 Received: from hades.apache.org (localhost [127.0.0.1]) by hades.apache.org (ASF Mail Server at hades.apache.org) with ESMTP id 95DBBAC0143; Fri, 9 Jan 2015 22:35:30 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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... Date: Fri, 09 Jan 2015 22:35:30 -0000 To: commits@lucene.apache.org From: ehatcher@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20150109223530.95DBBAC0143@hades.apache.org> 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 @@ + + 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 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 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 loaders = new ArrayList(); + 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 , 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
#parse("error.vm") @@ -7,11 +5,11 @@
- Find: - + $resource.find: + - #if($request.params.get('debugQuery')) + #if($debug) ## TODO: this would automatically happen when arbitrary parameters are kept on URLs #end #foreach($fq in $request.params.getParams('fq')) @@ -19,7 +17,7 @@ #end
- #foreach($fq in $params.getParams('fq')) + #foreach($fq in $request.params.getParams('fq')) #set($previous_fq_count=$velocityCount - 1) #if($fq != '') > @@ -29,7 +27,7 @@
- #if($request.params.get('debugQuery')) + #if($debug) toggle parsed query #end @@ -46,11 +44,10 @@ $page.results_found results found in - ${response.responseHeader.QTime} ms + ${response.responseHeader.QTime}ms - Page $page.current_page_number - of $page.page_count + $resource.page_of.insert($page.current_page_number,$page.page_count)
## Render Results, actual matching docs @@ -59,13 +56,12 @@
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 )

ERROR $response.response.error.code

$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 @@
Options: - #if($request.params.get('debugQuery')) + #if($debug) disable debug #else enable debug #end - - XML results + XML results ## TODO: Add links for other formats, maybe dynamically?
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 @@ *# Solr browse: #core_name + + +