I currently have my web.xml configured to catch 404s and send them to my spring controller which will perform a search given the original URL request.
The functionality is all there as far as the catch and search go, however the trouble begins to arise when I try to return a view.
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" p:order="1">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="jsp" value="text/html" />
</map>
</property>
<property name="defaultContentType" value="application/json" />
<property name="favorPathExtension" value="true" />
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value="" />
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
This is a snippet from my MVC config file.
The problem lies in resolving the view's path to the /WEB-INF/jsp/ directory. Using a logger in my JBoss setup, I can see that when I test this search controller by going to a non-existent page, the following occurs:
Server can't find the request
Request is sent to 404 error page (in this case my search controller)
Search controller performs search
Search controller returns view name (for this illustration, we'll assume test.jsp is returned)
Based off of server logger, I can see that org.springframework.web.servlet.view.JstlView is initialized once my search controller returns the view name (so I can assume it is being picked up correctly by the InternalResourceViewResolver)
Server attempts to return content to browser resulting in a 404!
A couple things confuse me about this:
I'm not 100% sure why this isn't resolving when test.jsp clearly exists under the /WEB-INF/jsp/ directory.
Even if there was some other problem, why would this result in a 404? Shouldn't a 404 error page that results in another 404 theoretically create an infinite loop?
Thanks for any help or pointers!
Controller class [incomplete]:
#Controller
public class SiteMapController {
//--------------------------------------------------------------------------------------
#Autowired(required=true)
private SearchService search;
#Autowired(required=true)
private CatalogService catalog;
//--------------------------------------------------------------------------------------
#RequestMapping(value = "/sitemap", method = RequestMethod.GET)
public String sitemap (HttpServletRequest request, HttpServletResponse response) {
String forwardPath = "";
try {
long startTime = System.nanoTime() / 1000000;
String pathQuery = (String) request.getAttribute("javax.servlet.error.request_uri");
Scanner pathScanner = new Scanner(pathQuery).useDelimiter("\\/");
String context = pathScanner.next();
List<ProductLightDTO> results = new ArrayList<ProductLightDTO>();
StringBuilder query = new StringBuilder();
String currentValue;
while (pathScanner.hasNext()) {
currentValue = pathScanner.next().toLowerCase();
System.out.println(currentValue);
if (query.length() > 0)
query.append(" AND ");
if (currentValue.contains("-")) {
query.append("\"");
query.append(currentValue.replace("-", " "));
query.append("\"");
}
else {
query.append(currentValue + "*");
}
}
//results.addAll(this.doSearch(query.toString()));
System.out.println("Request: " + pathQuery);
System.out.println("Built Query:" + query.toString());
//System.out.println("Result size: " + results.size());
long totalTime = (System.nanoTime() / 1000000) - startTime;
System.out.println("Total TTP: " + totalTime + "ms");
if (results == null || results.size() == 0) {
forwardPath = "home.jsp";
}
else if (results.size() == 1) {
forwardPath = "product.jsp";
}
else {
forwardPath = "category.jsp";
}
}
catch (Exception ex) {
System.err.println(ex);
}
System.out.println("Returning view: " + forwardPath);
return forwardPath;
}
}
1 . I'm not 100% sure why this isn't resolving when test.jsp clearly exists under the /WEB-INF/jsp/
directory.
This is because you did configure your view resolver with suffix = "" so the file must be named test (no extension).
2 . Even if there was some other problem, why would this result in a 404? Shouldn't a 404 error page that
results in another 404 theoretically create an infinite loop?
I'm pretty sure this's the result of some protection against the infinite redirect loop in spring MVC.
Note: in controllers, spring expect a view name as a result so test not test.jsp (or better, use ModelAndView)
I am posting this as answer because it is too long but it is probably not an answer.
http://localhost:8080/webapp/servlet-mapping-url/controller-mapping/method-mapping
if your controller's method which handles the request does not return a view name string or a view object or write directly to output stream, spring dispatcher should resolve the view name to /WEB-INF/jsp/controller-mapping/method-mapping.jsp
This means the jsp must be under a folder, named /WEB-INF/jsp/controller-mapping/. However, if a view name or view object is return by the controller method, spring dispatcher will uses that instead.
Ther are other many possible mapping combination but this is the most common one. If you could show your controller class, it will be easier.
Update
If you are using DefaultAnnotationHandlerMapping, you should always annotated your class with #RequestMapping(value = "/mapping-string"). Otherwise spring dispatcher will try to pick it up when nothing else is matched.
Since the controller is mapped, you will have to change the method mapping to value = {"", "/"}
For the returning view name, you don't need to put .jsp.
If the returning view name is home then the spring dispatcher will resolve to /WEB-INF/jsp/home.jsp
If the returning view name is path/home then the spring dispatcher will resolve to /WEB-INF/jsp/path/home.jsp
P.S. You used a word forwardPath but it isn't really a forward. It is just a view name.
#Controller
#RequestMapping(value = "/sitemap")
public class SiteMapController {
#RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
public String sitemap (HttpServletRequest request, HttpServletResponse response) {
...
if (results == null || results.size() == 0) {
forwardPath = "home";
}
else if (results.size() == 1) {
forwardPath = "product";
}
else {
forwardPath = "category";
}
...
return forwardPath;
}
}
Try to set order for your ViewResolver. For Example:
#Bean
public ViewResolver getViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setViewClass(JstlView.class);
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
resolver.setOrder(1);
return resolver;
}
Related
I'm trying to do a RequestMapping for URL from the Resources file to be variable according to the current Locale
I tried to use the PlaceHolders but i know it should load from Properties files . in addition to i have to load it as Bean during the run time thus it will load one time only with the default Locale so even if the changed the Locale , it will keep loading from the default Locale > en_US
Any Ideas ?
My Tries :
public class CustomPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
setProperties(convertResourceBundleToProperties(ResourceBundle.getBundle("urls", LocaleContextHolder.getLocale())));
super.postProcessBeanFactory(beanFactory);
}
}
and calling at in a Bean :
#Bean
public CustomPropertyPlaceholderConfigurer CustomPropertyPlaceholderConfigurer(){
return new CustomPropertyPlaceholderConfigurer();
}
Resources urls_ab.properties:
url.controller1=test
Controller :
#RequestMapping(value = "/${url.controller1}", method = RequestMethod.GET)
public String dd(ModelMap model){
return "__front_container";
}
When you make a change to your properties files that back your PropertyPlaceholderConfigurer, you will need to 'refresh' your application for changes to take effect. If you use a ConfigurableApplicationContext as your context, then you may call refresh on your context. The challenge is that in a web application, you will depend on your web.xml and not directly on the context object so refreshing to load the new/updated properties would require an application restart...or going through many unnecessary hoops. Consider the below which is an example within a Spring Webflow application. The locale is updated via the use of an interceptor. :
public class MyLocaleChangeInterceptor extends org.springframework.web.servlet.i18n.LocaleChangeInterceptor {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
Locale locale = (Locale) WebUtils.getSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME);
if (locale != null) {
try {
response.setLocale(locale);
} catch (Exception ex) {
response.setLocale(Locale.ENGLISH);
}
} else {
response.setLocale(Locale.ENGLISH);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
super.postHandle(request, response, handler, modelAndView);
}
}
/** https://gist.github.com/jkuipers/3537965 Spring LocaleResolver that uses cookies but falls back to the HTTP Session when cookies are disabled*/
public class MyCookieLocaleResolver extends CookieLocaleResolver {
private SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
#Override
protected Locale determineDefaultLocale(HttpServletRequest request) {
return sessionLocaleResolver.resolveLocale(request);
}
#Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
if (locale != null) {
try {
response.setLocale(locale);
} catch (Exception ex) {
response.setLocale(Locale.ENGLISH);
}
} else {
response.setLocale(Locale.ENGLISH);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
super.setLocale(request, response, locale);
sessionLocaleResolver.setLocale(request, response, locale);
}
#Override
public void setDefaultLocale(Locale defaultLocale) {
sessionLocaleResolver.setDefaultLocale(defaultLocale);
}
}
<!--Then the XML:-->
<bean id="localeChangeInterceptor" class="MyLocaleChangeInterceptor">
<property name="paramName" value="lang"/>
</bean>
<!-- Saves a locale change using a cookie -->
<bean id="localeResolver" class="MyCookieLocaleResolver" >
<property name="defaultLocale" value="en" />
</bean>
<!--Then Spring-webflow specific XML settings:-->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
<property name="order" value="2"/>
<property name="flowRegistry" ref="flowRegistry" />
<property name="interceptors" >
<list>
<ref local="localeChangeInterceptor" />
</list>
</property>
<property name="defaultHandler">
<bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
</property>
</bean>
If using Spring MVC (without spring webflow), see here for a brilliant solution: Spring MVC LocaleChangeInterceptor annotation based doesn't work
MyKong also provides a good solution: http://www.mkyong.com/spring-mvc/spring-mvc-internationalization-example/
I am new to String, and now facing some staring issues with Spring MVC.
In my application I have view resolver which maps view names to respective JSP files.
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix"><value>/WEB-INF/pages/</value></property>
<property name="suffix"><value>.jsp</value></property>
<property name="order" value="1" />
</bean>
It is working as expected, but now need to call a method in controller and show returned string in view.
my URL of request looks like http://localhost:8015/demo/greet
And the method in my controller to server this request is
#RequestMapping("/greet")
public String user(User user) {
return "Hi User";
}
When i call this url from browser, given method in browser get invoked, and when it returns a string, InternalResourceViewResolver try to find a page /WEB-INF/pages/greet.jsp, and as it doesn't exist, user gets 404 error. How can i send raw String from my controller method to browser?
Just change your controller code as below
#RequestMapping("/greet")
public #ResponseBody String user(User user) {
return "Hi User";
}
See documentation of ResponseBody here
Try:
#RequestMapping("/greet")
public #ResponseBody String user(User user) {
return "Hi User";
}
I am trying to find a best practice solution for my situation. I have two different sets of urls in my webapp that need to be secured/authenticated against two different sets of user bases.
For example, the /foo/* urls are to be accessible only to a certain set of users based on username/pwd tokens. And another set /bar/* urls are to be accessible only to a set of users (stored in a different database table from the first set) via username/pwd token mechanism.
I want to understand how to structure the shiro filter(s) in my web.xml and the corresponding shiro filter bean definition in my applicationContext.xml. Should I be using two different shiro filters (one for each url pattern), mapping to two different bean in the context file, where each bean has its own security manager that is configured with its own realm (each realm responsible for authenticating against the corresponding user base table).
Here I am following this two links:
multi-tenancy-in-shiro
Multiple-security-managers...
but I am getting errors in my application. Here I am pasting some sample code.
ApplicationContext-shiro.xml
<!-- Single realm app (realm configured next, below). If you have multiple
realms, use the 'realms' property instead. -->
<property name="realms">
<util:list>
<ref bean="adminAuthRealm" />
<ref bean="vendorAuthRealm" />
</util:list>
</property>
<!-- <property name="authenticator">
<bean class="com.yatra.mp.security.MultiLoginAuthenticator"/>
</property> -->
<property name="cacheManager" ref="cacheManager" />
<!-- Uncomment this next property if you want heterogenous session access
or clusterable/distributable sessions. The default value is 'http' which
uses the Servlet container's HttpSession as the underlying Session implementation. -->
</bean>`<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="unauthorizedUrl" value="/permissionDenied.do" />
<!-- The 'filters' property is usually not necessary unless performing
an override, which we want to do here (make authc point to a PassthruAuthenticationFilter
instead of the default FormAuthenticationFilter: -->
<property name="filters">
<util:map>
<entry key="adminAuthc" value-ref="adminAuthc" />
<entry key="vendorAuthc" value-ref="vendorAuthc" />
<entry key="adminUser" value-ref="adminUser" />
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/admin/welcome.do = anon
/vendor/welcome.do = anon
/vendor/signup.do = anon
/admin/signup.do = anon
/assets/** = anon
/admin/** = adminAuthc
/vendor/** = vendorAuthc
</value>
</property>
</bean>
<bean id="adminAuthc"
class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter">
<property name="loginUrl" value="/admin/login.do" />
<property name="successUrl" value="/admin/home.do" />
</bean>
<bean id="vendorAuthc"
class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter">
<property name="loginUrl" value="/vendor/login.do" />
<property name="successUrl" value="/vendor/home.do" />
</bean>
<bean id="adminUser" class="org.apache.shiro.web.filter.authc.UserFilter">
<property name="loginUrl" value="/admin/login.do" />
</bean>
<bean id="vendorUser" class="org.apache.shiro.web.filter.authc.UserFilter">
<property name="loginUrl" value="/vendor/login.do" />
</bean>`
MultiLoginAuthenticator.java
public class MultiLoginAuthenticator extends ModularRealmAuthenticator {
#Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured(); <------- Here I am getting null getRealms(). So I am getting exceptions
MultiLoginAuthenticationToken mlat = null;
Realm loginRealm = null;
if (!(authenticationToken instanceof MultiLoginAuthenticationToken)) {
throw new AuthenticationException("Unrecognized token , not a typeof MultiLoginAuthenticationToken ");
} else {
mlat = (MultiLoginAuthenticationToken) authenticationToken;
loginRealm = lookupRealm(mlat.getRealmName());
}
return doSingleRealmAuthentication(loginRealm, mlat);
}
protected Realm lookupRealm(String realmName) throws AuthenticationException {
Collection<Realm> realms = getRealms();
for (Realm realm : realms) {
if (realm.getName().equalsIgnoreCase(realmName)) {
return realm;
}
}
throw new AuthenticationException("No realm configured for Client " + realmName);
}
}
while i am configuring MultiLoginAuthenticator, i am getting null in getReamls() method. So it is throwing IllegalStateException.
If I remove this Authenticator configuration from applicationContext.xml file, then it is able to login correctly but it is calling different Realm for "doGetAuthorizationInfo" method. So It is not assigning any role and permission to that current Subject.
This is my Realm file (both Realms are same. Difference is both are calling different datasource.)
#Override
public boolean supports(AuthenticationToken token) {
if (token instanceof MultiLoginAuthenticationToken) {
return ((MultiLoginAuthenticationToken)token).getRealmName().equalsIgnoreCase("VendorAuthRealm");
}
return false;
}
#Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
logger.debug("authorization info.....");
logger.debug("realm size is : {}", principals.fromRealm(getName()).size());
logger.debug("realm name is : {}", principals.fromRealm(getName()));
int userId = (Integer) principals.fromRealm(getName()).iterator().next();
VendorUser vendorUser = vendorUserService.getVendorUser(userId);
if(vendorUser != null) {
logger.debug("vendor user first name is : {}", vendorUser.getFirstName());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if(vendorUser.getVendorProducts() != null){
logger.debug("vendor users products and rate types...");
info.addRoles(VendorYatraUtil.getProductSet(vendorUser.getVendorProducts()));
info.addStringPermissions(VendorYatraUtil.getCarRateTypeSet(vendorUser.getVendorCarRateTypes()));
}
return info;
}
return null;
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
MultiLoginAuthenticationToken token = (MultiLoginAuthenticationToken) authcToken;
logger.debug("username is : {}", token.getUsername());
if(vendorUserService.findVendorUser(token.getUsername()) != null){
VendorUser vendorUser = vendorUserService.findVendorUser(token.getUsername());
if (vendorUser != null){
return new SimpleAuthenticationInfo(vendorUser.getId(),
vendorUser.getPassword(), getName());
}
}
return null;
}
Thanks,
Ankit
I have solved this problem now.
Here in applicationContext.xml file is having order misplace for Custom authenticator class. I wrote authenticator property first and then define Realms.
It is authenticating successfully...
guys,
At first, I show you the part of applicationContext.xml on ingerating HttpServer.
And I read the construcotr arguments from the solr.properties.
<!-- solr configuration-->
<bean id="solrHttpServer" class="com.augmentum.ksp.solr.server.SolrHttpServer" scope="prototype">
<constructor-arg value="${solr.url}" />
<constructor-arg value="${solr.socketTimeOut}" />
<constructor-arg value="${solr.connTimeOut}" />
<constructor-arg value="${solr.maxConnDefault}" />
<constructor-arg value="${solr.maxConnTotal}" />
<constructor-arg value="${solr.maxRetries}" />
<constructor-arg value="${solr.allowCompression}" />
<constructor-arg value="${solr.followRedirects}" />
</bean>
And following is our query method in DAO layer, we have many methods in service layer use the "queryCount" method at the same time.
#Repository
public class SolrBaseDaoImpl implements SolrBaseDao {
#Autowired
private SolrHttpServer solrHttpServer;
#Override
public synchronized int queryCount(String coreName, String queryExpression, Set<String> filterQuerys)
throws SolrServerException, IOException {
if (null == queryExpression || "".equals(queryExpression)) {
return 0;
}
String baseUrl = SolrUtil.getSolrBaseURL(solrHttpServer.getBaseURL());
solrHttpServer.setBaseURL(baseUrl + "/" + coreName);
SolrQuery query = new SolrQuery();
query.setQuery(queryExpression);
// set filter query
if (null != filterQuerys) {
for (String fq : filterQuerys) {
query.addFilterQuery(fq);
}
}
query.setStart(0);
query.setRows(Integer.MAX_VALUE);
QueryResponse rsp = solrHttpServer.query(query);
return rsp.getResults().size();
}
}
Thus, a problem occurred. Reported that "SolrRemoteException:undefined field *".
We think that the source of the error is that there is a lot of request access the "queryCount" method **at the same time, thus we add a "synchronized" decorateor to the method, so that the problem nerver occurred any more.
But, as we all know, when there is a lot of request access the method one by one, which perfom very slowly. That is terrible!
Could you tell me your soultion to the problem ? or the error is not caused by the thread synchronization?
When I update the bean:
<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".vm"/>
<property name="toolboxConfigLocation" value="tools.xml" />
</bean>
With the tools.xml path for Velocity Tools, I get:
Caused by:
java.lang.ClassNotFoundException: org.apache.velocity.tools.view.ToolboxManager
I've tried plugging in tools version 2 and 1.4, neither have this package structure. Did I miss something obvious? What version of Velocity Tools is the Spring/Velocity component supporting?
I use a little bit simpler of a way. I also cannot force Velocity Tools to work due to lack of configuration documentation and examples. I just get the velocity-generic-tools-2.0.jar and make a little change in my view resolver:
<bean id="velocityViewResolver"
class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="order" value="1"/>
<property name="prefix" value="/WEB-INF/vm/"/>
<property name="suffix" value=".vm"/>
<property name="exposeSpringMacroHelpers" value="true"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
<property name="attributesMap">
<map>
<!--Velocity Escape Tool-->
<entry key="esc"><bean class="org.apache.velocity.tools.generic.EscapeTool"/></entry>
</map>
</property>
</bean>
Then, in the velocity template you can use it as usual $esc.html($htmlCodeVar). This solution is very simple, without tons of configs and overriding spring classes.
Spring has very outdated Velocity support by default. I extend VelocityView class from Spring and override createVelocityContext method where I initialize Tools myself. Here is how it looks at the end.
With 3.0.5 I used a similar class to what serg posted, with the only modification being to use the updated classes which spring did not use (tail through VelocityToolboxView -> ServletToolboxManager (used in the createVelocityContext we have overridden) That is the class which is deprecated, so I modified the initVelocityToolContext in serg's answer to be:
private ToolContext getToolContext() throws IllegalStateException, IOException {
if (toolContext == null) {
XmlFactoryConfiguration factoryConfiguration = new XmlFactoryConfiguration("Default Tools");
factoryConfiguration.read(getServletContext().getResourceAsStream(getToolboxConfigLocation()));
ToolboxFactory factory = factoryConfiguration.createFactory();
factory.configure(factoryConfiguration);
toolContext = new ToolContext();
for (String scope : Scope.values()) {
toolContext.addToolbox(factory.createToolbox(scope));
}
}
return toolContext;
}
I also had to change the line which created the VelocityContext to call this method obviously.
My bean now looks like:
<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver"
p:cache="false"
p:prefix=""
p:suffix=".vm"
p:layoutUrl="templates/main.vm"
p:toolboxConfigLocation="/WEB-INF/velocity/velocity-toolbox.xml"
p:viewClass="path.to.overriden.class.VelocityToolsLayoutView"
/>
Inspired by answers from Scott and serg, here's another way to do it that does not require XML: http://squirrel.pl/blog/2012/07/13/spring-velocity-tools-no-xml/
public class MyVelocityToolboxView extends VelocityView {
#Override
protected Context createVelocityContext(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response) {
ViewToolContext context = new ViewToolContext(getVelocityEngine(),
request, response, getServletContext());
ToolboxFactory factory = new ToolboxFactory();
factory.configure(ConfigurationUtils.getVelocityView());
for (String scope : Scope.values()) {
context.addToolbox(factory.createToolbox(scope));
}
if (model != null) {
for (Map.Entry<String, Object> entry : (Set<Map.Entry<String, Object>>) model
.entrySet()) {
context.put(entry.getKey(), entry.getValue());
}
}
return context;
}
}
Inspired by all the answers above, this is my implementation of VelocityLayoutView for spring and velocity-tools 2.0, added some improvement!
public class VelocityToolsView extends VelocityLayoutView {
private static final String TOOL_MANAGER_KEY = ViewToolManager.class.getName();
#Override
protected Context createVelocityContext(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {
ServletContext application = getServletContext();
// use a shared instance of ViewToolManager
ViewToolManager toolManager = (ViewToolManager)application.getAttribute(TOOL_MANAGER_KEY);
if(toolManager == null) {
toolManager = createToolManager(getVelocityEngine(), getToolboxConfigLocation(), application);
application.setAttribute(TOOL_MANAGER_KEY, toolManager);
}
ViewToolContext toolContext = toolManager.createContext(request, response);
if(model != null) { toolContext.putAll(model); }
return toolContext;
}
private ViewToolManager createToolManager(VelocityEngine velocity, String toolFile, ServletContext application) {
ViewToolManager toolManager = new ViewToolManager(application, false, false);
toolManager.setVelocityEngine(velocity);
// generic & view tools config
FactoryConfiguration config = ConfigurationUtils.getVelocityView();
// user defined tools config
if(toolFile != null) {
FactoryConfiguration userConfig = ConfigurationUtils.load(application.getRealPath(toolFile));
config.addConfiguration(userConfig);
}
toolManager.configure(config);
return toolManager;
}
}
I found that this variation on #serg's technique worked for me.