Static static resources going through DispatcherServlet - spring

In my Spring webapp I have annotated my dispatcher servlet like this:
<tx:annotation-driven />
<mvc:annotation-driven />
<context:annotation-config />
<context:component-scan base-package="com.myapp.package.controller" />
<mvc:resources location="/css/" mapping="/css/**" />
<mvc:resources location="/script/" mapping="/script/**" />
<mvc:resources location="/img/" mapping="/img/**" />
<mvc:resources location="/fonts/" mapping="/fonts/**" />
<!-- Uso de Tiles -->
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles-defs.xml</value>
</list>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView" />
</bean>
Now by activating spring's debug mode on log4j.properties, I've checked that every resource (even static resources) goes through DispatcherServlet, which I don't even know if it's expected behavior. The fact is, by debugging the processRequest method I've checked that this method is somehow causing high memory consumption on serving each webpage, my guess being that every static resource is being held in memory by this method.
This is the log output for a static resource such as jquery script library:
14:14:23,569 DEBUG RequestMappingHandlerMapping:220 - Looking up handler method for path /script/jquery/jquery-1.9.1.min.js
14:14:23,575 DEBUG RequestMappingHandlerMapping:230 - Did not find handler method for [/script/jquery/jquery-1.9.1.min.js]
14:14:23,576 DEBUG SimpleUrlHandlerMapping:169 - Matching patterns for request [/script/jquery/jquery-1.9.1.min.js] are [/script/**]
14:14:23,576 DEBUG SimpleUrlHandlerMapping:194 - URI Template variables for request [/script/jquery/jquery-1.9.1.min.js] are {}
14:14:23,576 DEBUG SimpleUrlHandlerMapping:124 - Mapping [/script/jquery/jquery-1.9.1.min.js] to HandlerExecutionChain with handler [org.springframework.web.servlet.resource.ResourceHttpRequestHandler#101f75b5] and 1 interceptor
14:14:23,576 DEBUG DispatcherServlet:912 - Last-Modified value for [/myapp/script/jquery/jquery-1.9.1.min.js] is: -1
14:14:23,576 DEBUG ResourceHttpRequestHandler:173 - Trying relative path [jquery/jquery-1.9.1.min.js] against base location: ServletContext resource [/script/]
14:14:23,577 DEBUG ResourceHttpRequestHandler:178 - Found matching resource: ServletContext resource [/script/jquery/jquery-1.9.1.min.js]
14:14:23,577 DEBUG ResourceHttpRequestHandler:132 - Determined media type 'application/javascript' for ServletContext resource [/script/jquery/jquery-1.9.1.min.js]
As far as I can tell it's properly identifying it as a static resource.
Is my configuration right? Shouldn't static resources be served directly instead of going throuhg Dispatcher Servlet? Could it be that every resource is being kept in memory on serving it to the response?

<mvc:resources location="/css/" mapping="/css/**" />
means that the dispatcher servlet will, indeed, serve static resources located in /css for any URL starting with /css/. This is what allows serving static resources from the classpath, for example, instead of serving them from static files in the webapp.
If you don't want the Spring servlet to serve static resources, the do as the documentation indicates:
<mvc:default-servlet-handler/>

If you've done configured something with Spring, Spring will handle it.
When you provide a config with this element
<mvc:resources location="/css/" mapping="/css/**" />
Spring registers a SimpleUrlHandlerMapping bean with a mapping between the specified mapping and a ResourceHttpRequestHandler for the location.
The DispatcherServlet registers all HandlerMapping beans it finds in the ApplicationContext. One of these will be the SimpleUrlHandlerMapping above. If a request arrives which the SimpleUrlHandlerMapping can handle, the DispatcherServlet will use it. The SimpleUrlHandlerMapping will then delegate to the appropriate ResourceHttpRequestHandler which will serve the static resource.
Could it be that every resource is being kept in memory on serving it
to the response?
No, the ResourceHttpRequestHandler does not cache the content of the resource.

Related

Configuring hessian service in karaf with spring dm

With the environment:
Karaf 3.0.1
Spring 3.2.4
Hessian 4.0.33
I have already exposed a service via CXF and now I'm trying to expose the same service as a Hessian Service.
There is no war or web.xml, just plain beans + pax-http and I have tried the following:
<bean name="/hessian" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="promocionalOnLineWebServiceBean"/>
<property name="serviceInterface" value="org.fideliapos.promos.webservice.PromocionalOnLineFacade"/>
</bean>
...
<bean id="hessianServlet" class="org.springframework.web.context.support.HttpRequestHandlerServlet"/>
...
<osgi:service ref="hessianServlet" interface="javax.servlet.http.HttpServlet">
<service-properties>
<entry key="alias" value="/hessian"/>
</service-properties>
</osgi:service>
The idea is to register a servlet (a HttpRequestHandlerServlet) whose target is a HessianServiceExporter but I'm getting a No WebApplicationContext found: no ContextLoaderListener registered?.
I have traced the spring code and the internal jetty is recognizing the servlet and calling its init method:
#Override
public void init() throws ServletException {
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
this.target = wac.getBean(getServletName(), HttpRequestHandler.class);
}
and here lies the problem, since there is no spring WebApplicationContext and the target property cannot be inyected.
Am I missing something? or it is not possible to make it work like this.
As a workaround I'm considering extending the Servlet with my own implementation (setTarget and so) but I'd rather not to do it.
UPDATE
After trying to create and add my own HttpContext there is still something missing:
I implemented my own HttpContext:
public class HessianContext implements HttpContext{
...
}
added the bean
<bean id="hessianContext" class="org.fideliapos.promos.hessian.HessianContext"/>
the service:
<osgi:service id="hessianContextService" ref="hessianContext" interface="org.osgi.service.http.HttpContext">
<service-properties>
<entry key="httpContext.id" value="hessian"/> <!-- also tried with contextId-->
</service-properties>
</osgi:service>
and finally the servlet as a service:
<osgi:service ref="hessianServlet" interface="javax.servlet.http.HttpServlet">
<service-properties>
<entry key="alias" value="/hessian"/>
<entry key="httpContext.id" value="hessian"/> <!-- also tried with contextId-->
</service-properties>
</osgi:service>
Since the init method is looking for a WebApplicationContext it looks like I should declare and explicit GenericWebApplicationContext bean but I dont know how to 'join' this bean with the required HttpContext for OSGi.
Looks like you need to add the Spring WebApplicationContext to the HttpContext used for your servlet. Right now you use the DefaultHttpContext of Pax Web. In your case you'll need to register a custom HttpContext that is aware of the Spring stuff so the WebApplicationContextUtils.getRequireWebApplicationContext is capable of extracting this information.
For this you'll need to register your custom HttpContext as Service and reference it in your Servlet, a complete Example of this using Blueprint (similar to spring) can be found here
Following is an excerpt of it:
<service id="forbiddenCtxtService" ref="forbiddenContext" interface="org.osgi.service.http.HttpContext">
<service-properties>
<entry key="httpContext.id" value="forbidden"/>
</service-properties>
</service>
The Important part is the httpContext.id
<bean id="forbiddenServlet" class="org.ops4j.pax.web.extender.samples.whiteboard.internal.WhiteboardServlet">
<argument type="java.lang.String" value="/forbidden"/>
</bean>
<service id="forbiddenServletService" ref="forbiddenServlet" interface="javax.servlet.Servlet">
<service-properties>
<entry key="alias" value="/forbidden"/>
<entry key="httpContext.id" value="forbidden"/>
</service-properties>
</service>
Again here the registered Servlet does have a configuration for the corresponding httpContext.id, this binds this Servlet to the previously registered HttpContext.

Jersey resources mapping

Is it possible do the following by using jersey?
<mvc:resources mapping="/css/**" location="/public/css/" />
<mvc:resources mapping="/js/**" location="/public/js/" />
<mvc:resources mapping="/views/**" location="/public/views/" />
So, i need to handle /css/some.css like /public/css/some.css
Thanks!
Jersey itself doesn't serve static resources, presumably you're integrating with Spring that should work already as long as you registered the dispatch servlet and you don't have a JAX-RS resource at the top level like #Path("/"), subset them to #Path("/services") for example.

Moving mvc interceptor from module-context to web-application context

Can we move mvc interceptors from individual context file to web-application context.xml
Thus removing interceptor from individual module context?
You can put them in the web-context as follow:
<mvc:interceptors>
<bean class="com.package.TheInterceptor" />
<bean class="com.package.AnotherInterceptor" />
</mvc:interceptors>

how Spring MVC find its handler mapping

in Spring MVC we can configure handler mapping as bean.but how spring examine what is the handler mapping we mentioned in xml?
simpliy
<bean id="simplehandler" class="" />
do we need to specify "simplehandler" bean id to somewhere for spring to identify bean handler?
First thing that must be clear is that: Spring has several handler mappings.
And the "DefaulAnnotationHandlerMapping" is activated by default (See DispatcherServlet.properties in the Spring distribution or just google for it. All default handlers are listed there). Spring will choose "DefaulAnnotationHandlerMapping" by default.
If you want Spring to use another handler mapping strategy, you have to tell him explicitly
e.g.:
<bean class="org.blablabla......ControllerClassNameHandlerMapping" />
Note that this cancels the use of the default handler mapping strategy
You can also tell Spring to use several handler mapping strategy and prioritized them by using the order property in the mappers declaration
something like
<bean class="org.blabla....DefaulAnnotationHandlerMapping" >
<property name="order" value="0"/>
</bean>
<bean class="org.blablabla......ControllerClassNameHandlerMapping">
<property name="order" value="1"/>
</bean>
Hope this helps. And sorry if the syntax of my bean declaration is not 100% correct. I had to write quickly ;-)

Absolute path in <mvc:resources/> instead of path relative to mapping of spring servlet

I'm trying to map a request to static resources in a spring environment. My app server is Jetty.
In web.xml, I'm mapping various url patterns to my spring servlet:
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/otherpath/*</url-pattern>
</servlet-mapping>
[many more mappings...]
Note that "/" is not mapped to my spring servlet.
In spring-servlet.xml, I'm using the mvc:resources tag to map a url to a directory with my static content:
<mvc:resources mapping="/static/**" location="/WEB-INF/static/" />
This does not work as I expected. Instead of mapping
/static/ to /WEB-INF/static/,
it maps
/static/static/ to /WEB-INF/static
The reason is that the mapping given in "mvc:resources" seems to not be relative to / but relative to the path that maps to the spring servlet.
Is there a way to consider the full path, relative to / for the mapping, not the path relative to the servlet mapping?
The solution is to not use the mvc:resources tag, but to configure the corresponding handler with a bean and a URLHandlerMapping:
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="alwaysUseFullPath" value="true"/>
<property name="mappings">
<props>
<prop key="/static/*">staticResources</prop>
</props>
</property>
</bean>
<bean id="staticResources" class="org.springframework.web.servlet.resource.ResourceHttpRequestHandler">
<property name="locations">
<list>
<value>/WEB-INF/static/</value>
</list>
</property>
</bean>
The SimpleUrlHandlerMapping with its alwaysUseFullPath property does allow more fine-grained control over the mapping.
To answer your question with one short word. No, at least I don't think so.
The servlet looks inside it's own "space" and that is after the servlet-mapping done in web.xml. That in turn is after the mapping done i your container (like tomcat)
Would it be possible to add just one servlet to / and then add two <mvc:resource />? One with /static/** and one with /otherpath/** (or whatever you need there). If not I would go with JB Nizet's solution to add two different servlets entirely.
Or you could use <mvc:default-servlet-handler/> and <spring:url>. It worked for me.
mvc:resources doesn't seem to work when application is not started on the ROOT context.
Here's the configuration that I used (note the commented bit that was doing the resource mapping to the application started under 'localhost:8080/myapp' context, though context name shouldn't be in the spring config):
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources -->
<!--<mvc:resources location="/styles" mapping="/myapp/styles/**"/>-->
<!--<mvc:resources location="/js" mapping="/myapp/js/**"/>-->
<!-- Allows for mapping the DispatcherServlet to "/" by forwarding static resource
requests to the container's default Servlet -->
<mvc:default-servlet-handler/>
The trick is to use spring:url to resolve your application context. Here is what I used for that:
<spring:url value="/styles/site.css" var="site_style"/>
<link rel="stylesheet" href="${site_style}" type="text/css" media="screen"/>
I'm basically using relative paths to my root app folder, while spring takes care of adding the /myapp in front of it.
It's still pretty strange that mvc:resources doesn't do that on it's own, but at least this works and its still pretty neat.

Resources