SOLR-7915: Provide pluggable context tool support for VelocityResponseWriter

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1700691 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Erik Hatcher 2015-09-02 00:12:56 +00:00
parent cf3644e72b
commit 06c9ed5c5d
6 changed files with 109 additions and 4 deletions

View File

@ -144,6 +144,8 @@ New Features
* SOLR-4316: Add a collections dropdown to angular admin UI (Upayavira, Shalin Shekhar Mangar) * SOLR-4316: Add a collections dropdown to angular admin UI (Upayavira, Shalin Shekhar Mangar)
* SOLR-7915: Provide pluggable context tool support for VelocityResponseWriter (Erik Hatcher)
Bug Fixes Bug Fixes
---------------------- ----------------------

View File

@ -25,7 +25,9 @@ import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@ -45,7 +47,6 @@ import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.apache.velocity.tools.ConversionUtils; import org.apache.velocity.tools.ConversionUtils;
import org.apache.velocity.tools.generic.ComparisonDateTool; import org.apache.velocity.tools.generic.ComparisonDateTool;
import org.apache.velocity.tools.generic.ContextTool;
import org.apache.velocity.tools.generic.DisplayTool; import org.apache.velocity.tools.generic.DisplayTool;
import org.apache.velocity.tools.generic.EscapeTool; import org.apache.velocity.tools.generic.EscapeTool;
import org.apache.velocity.tools.generic.ListTool; import org.apache.velocity.tools.generic.ListTool;
@ -84,6 +85,7 @@ public class VelocityResponseWriter implements QueryResponseWriter, SolrCoreAwar
private static final Logger log = LoggerFactory.getLogger(VelocityResponseWriter.class); private static final Logger log = LoggerFactory.getLogger(VelocityResponseWriter.class);
private static final SolrVelocityLogger velocityLogger = new SolrVelocityLogger(log); private static final SolrVelocityLogger velocityLogger = new SolrVelocityLogger(log);
private Properties velocityInitProps = new Properties(); private Properties velocityInitProps = new Properties();
private Map<String,String> customTools = new HashMap<String,String>();
@Override @Override
public void init(NamedList args) { public void init(NamedList args) {
@ -112,6 +114,14 @@ public class VelocityResponseWriter implements QueryResponseWriter, SolrCoreAwar
solrResourceLoaderEnabled = (null == srle ? true : srle); solrResourceLoaderEnabled = (null == srle ? true : srle);
initPropertiesFileName = (String) args.get(PROPERTIES_FILE); initPropertiesFileName = (String) args.get(PROPERTIES_FILE);
NamedList tools = (NamedList)args.get("tools");
if (tools != null) {
for(Object t : tools) {
Map.Entry tool = (Map.Entry)t;
customTools.put(tool.getKey().toString(), tool.getValue().toString());
}
}
} }
@Override @Override
@ -183,10 +193,8 @@ public class VelocityResponseWriter implements QueryResponseWriter, SolrCoreAwar
private VelocityContext createContext(SolrQueryRequest request, SolrQueryResponse response) { private VelocityContext createContext(SolrQueryRequest request, SolrQueryResponse response) {
VelocityContext context = new VelocityContext(); VelocityContext context = new VelocityContext();
context.put("request", request);
// Register useful Velocity "tools" // Register useful Velocity "tools"
context.put("log", log); // TODO: add test context.put("log", log); // TODO: add test; TODO: should this be overridable with a custom "log" named tool?
context.put("esc", new EscapeTool()); context.put("esc", new EscapeTool());
context.put("date", new ComparisonDateTool()); context.put("date", new ComparisonDateTool());
context.put("list", new ListTool()); context.put("list", new ListTool());
@ -198,6 +206,25 @@ public class VelocityResponseWriter implements QueryResponseWriter, SolrCoreAwar
request.getCore().getSolrConfig().getResourceLoader().getClassLoader(), request.getCore().getSolrConfig().getResourceLoader().getClassLoader(),
request.getParams().get(LOCALE))); request.getParams().get(LOCALE)));
/*
// Custom tools, specified in config as:
<queryResponseWriter name="velocityWithCustomTools" class="solr.VelocityResponseWriter">
<lst name="tools">
<str name="mytool">com.example.solr.velocity.MyTool</str>
</lst>
</queryResponseWriter>
*/
// Custom tools can override any of the built-in tools provided above, by registering one with the same name
for(String name : customTools.keySet()) {
context.put(name, SolrCore.createInstance(customTools.get(name), Object.class, "VrW custom tool", request.getCore(), request.getCore().getResourceLoader()));
}
// custom tools _cannot_ override context objects added below, like $request and $response
// TODO: at least log a warning when one of the *fixed* tools classes in name with a custom one, currently silently ignored
// Turn the SolrQueryResponse into a SolrResponse. // Turn the SolrQueryResponse into a SolrResponse.
// QueryResponse has lots of conveniences suitable for a view // QueryResponse has lots of conveniences suitable for a view
// Problem is, which SolrResponse class to use? // Problem is, which SolrResponse class to use?
@ -221,6 +248,8 @@ public class VelocityResponseWriter implements QueryResponseWriter, SolrCoreAwar
rsp = new SolrResponseBase(); rsp = new SolrResponseBase();
rsp.setResponse(parsedResponse); rsp.setResponse(parsedResponse);
} }
context.put("request", request);
context.put("response", rsp); context.put("response", rsp);
return context; return context;

View File

@ -35,4 +35,20 @@
<queryResponseWriter name="velocityWithInitProps" class="solr.VelocityResponseWriter"> <queryResponseWriter name="velocityWithInitProps" class="solr.VelocityResponseWriter">
<str name="init.properties.file">velocity-init.properties</str> <str name="init.properties.file">velocity-init.properties</str>
</queryResponseWriter> </queryResponseWriter>
<queryResponseWriter name="velocityWithCustomTools" class="solr.VelocityResponseWriter">
<!-- Enable params resource loader to make tests leaner, no external template needed -->
<bool name="params.resource.loader.enabled">true</bool>
<lst name="tools">
<!-- how someone would typically add a custom tool, with a custom, non-clashing name -->
<str name="mytool">org.apache.solr.velocity.MockTool</str>
<!-- override the $log context object -->
<str name="log">org.apache.solr.velocity.MockTool</str>
<!-- Try to override response, but ignored -->
<str name="response">org.apache.solr.velocity.MockTool</str>
</lst>
</queryResponseWriter>
</config> </config>

View File

@ -0,0 +1 @@
$!mytool.star("foo")

View File

@ -0,0 +1,34 @@
package org.apache.solr.velocity;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.apache.solr.core.SolrCore;
public class MockTool {
private final SolrCore core;
public MockTool(SolrCore core) {
this.core = core;
}
public String star(String str) {
return "** " + str + " **";
}
public SolrCore getCore() { return core; }
}

View File

@ -119,6 +119,29 @@ public class VelocityResponseWriterTest extends SolrTestCaseJ4 {
assertEquals("", h.query(req("q","*:*", "wt","velocityWithInitProps",VelocityResponseWriter.TEMPLATE,"foreach"))); assertEquals("", h.query(req("q","*:*", "wt","velocityWithInitProps",VelocityResponseWriter.TEMPLATE,"foreach")));
} }
@Test
public void testCustomTools() throws Exception {
assertEquals("", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"custom_tool")));
assertEquals("** LATERALUS **", h.query(req("q","*:*", "wt","velocityWithCustomTools",VelocityResponseWriter.TEMPLATE,"t",
SolrParamResourceLoader.TEMPLATE_PARAM_PREFIX+"t", "$mytool.star(\"LATERALUS\")")));
// Does $log get overridden?
assertEquals("** log overridden **", h.query(req("q","*:*", "wt","velocityWithCustomTools",VelocityResponseWriter.TEMPLATE,"t",
SolrParamResourceLoader.TEMPLATE_PARAM_PREFIX+"t", "$log.star(\"log overridden\")")));
// Does $response get overridden? actual blank response because of the bang on $! reference that silences bogus $-references
assertEquals("", h.query(req("q","*:*", "wt","velocityWithCustomTools",VelocityResponseWriter.TEMPLATE,"t",
SolrParamResourceLoader.TEMPLATE_PARAM_PREFIX+"t", "$!response.star(\"response overridden??\")")));
// Custom tools can also have a SolrCore-arg constructor because they are instantiated with SolrCore.createInstance
// TODO: do we really need to support this? no great loss, as a custom tool could take a SolrCore object as a parameter to
// TODO: any method, so one could do $mytool.my_method($request.core)
// I'm currently inclined to make this feature undocumented/unsupported, as we may want to instantiate classes
// in a different manner that only supports no-arg constructors, commented (passing) test case out
// assertEquals("collection1", h.query(req("q","*:*", "wt","velocityWithCustomTools",VelocityResponseWriter.TEMPLATE,"t",
// SolrParamResourceLoader.TEMPLATE_PARAM_PREFIX+"t", "$mytool.core.name")));
}
@Test @Test
public void testLocaleFeature() throws Exception { public void testLocaleFeature() throws Exception {
assertEquals("Color", h.query(req("q", "*:*", "wt", "velocity", VelocityResponseWriter.TEMPLATE, "locale", assertEquals("Color", h.query(req("q", "*:*", "wt", "velocity", VelocityResponseWriter.TEMPLATE, "locale",