Serving Static Content from same directory as service - Dropwizard - filter

I am building a Dropwizard app to replace a legacy webserver, due to a lot of links pointing at this server, I have to be backwards compatible and support all kinds of resources that the legacy server used to serve.
The problem is that I have a bunch of static JS files being served from the same directory/path as the services themselves. Apparently you can't serve both types (service and static) from a single directory. so if I add the static bundle (now located in the resources directory), like so:
bootstrap.addBundle(new AssetsBundle("/javascript", "/media"));
The dynamic services (served from /media/) start returning 404
if I comment it, the dynamic services work fine.
As I said, I can't change any of the links, static or dynamic...
Any suggestions?
Update:
Resolved, here is a working example inspired by #airborn 's answer, this question which also pointed me to this module, which claims to solve this problem generically.
This example will apply a filter to ALL incoming requests, and allow you to redirect to wherever you want (similar to .htaccess functionality on apache).
Place the following line in the run function your main dropwizard class (the one that extends **Service<AppConfiguration> **
environment.addFilter(new UrlRewriteFilter(),"/*");
Place this class somewhere accessible to the above class
#WebFilter
public class UrlRewriteFilter implements Filter {
#Override
public void init(FilterConfig config) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
String requestURI = request.getRequestURI();
// Place Redirection logic here (else continues the filter chain).
if (redirectConditionTrue)
String newURI = ""//New URl to do to;
req.getRequestDispatcher(newURI).forward(req, res);
} else {
chain.doFilter(req, res);
}
}
#Override
public void destroy() {
//
}
}

Using Environment in your Service class, you should be able to add a filter to Jetty. In this proxy filter, you should be able to detect those js files and redirect them to the new location.

Related

Debug Jersey mapping/routing execution before reaching endpoints/resources

I have been working with Glassfish/Jackson for over a year and I always have this problem when introducing a new endpoint implementation: when the endpoint is not reached and I want to understand why, the only hints I have to go on are the returned request, since the execution doesn't reach the desired endpoint or resource (routing/mapping error).
I want to intercept the Jersey mapping/routing execution before reaching endpoints/resources, with the "raw" request, so that I can better understand resource/endpoint mapping and routing problems.
This answer to a different question, by #xeye, solved this problem for me:
Create a filter that implements ContainerRequestFilter, and override its filter method. This will be where we can intercept all requests for debugging.
// Executed whenever a request is sent to the API server.
// Useful for debugging errors that don't reach the desired endpoint implementation.
#Provider
#Priority(value = 0)
public class MyFilter implements ContainerRequestFilter {
#Context // request scoped proxy
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
try {
// put a breakpoint or log desired attributes of requestContext here.
} catch (Exception e) {
// ignore
}
}
}
Then register this new class in your ConfigResource implementation.
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig(){
register(MyFilter.class);
// ...
}
(It's OK to Ask and Answer Your Own Questions)

Copy RequestParams to RequestHeaders before handling in RestController

To replace a legacy system and not breaking the interface, I'm looking for a way to implement the following scenario:
If a REST client hasn't set a specific HTTP header (applicationId) but sends it as a query-paramter (aka RequestParameter), this value should be taken as a method parameter in a Spring Boot RestController.
The current method looks like this:
#RequestMapping(value = "/something", method = RequestMethod.GET)
public void doSomething(#RequestHeader("applicationId") String applicationId) { }
I think there could be two possible ways:
Annotate the method somehow to map a query-parameter OR a header to a method parameter
Write an Interceptor which reads all query-parameters of a request and set non-existing headers with their values. This way, the method wouldn't have to be touched at all.
In both ways I'm not sure how to implement them (don't know if 1. is even possible). I tried with an own HandlerInterceptor which reads query-params in preHandle (successfully) but isn't able to set headers in the request before it is forwarded to the RestController.
Write a Filter that wraps the incoming request using a HttpServletRequestWrapper. This wrapper should override the getHeader method.
public ParameterToHeaderWrappingRequestFilter extends OncePerRequestFilter {
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
filterChain.doFilter(new ParameterToHeaderWrappingRequest(request), response, filterChain);
}
}
Register this filter as a #Bean in your Spring Boot application and it will be applied automatically.
public class ParameterToHeaderWrappingRequest extends HttpServletRequestWrapper {
public String getHeader(String name) {
String header = super.getHeader(name);
if (header == null) {
header = getParameter(name);
}
return header;
}
}
Something like that should do the trick. Depending on your needs you might want/need to override some additional header based methods and you probably want to limit the number of headers to override with parameters.
The rest of your code can now be written as is.

Spring Session not working on Tomcat 8 when using Tiles - SESSION Cookie is not set as response is already included

I am using Spring Session 1.2.0.RELEASE on a Spring Boot Project. This is packaged as a war and deployed on Tomcat 8.
I have followed Spring Session documentation and configured it properly. The problem is that the entry point to the application is a controller that sets some value on session but the SESSION cookie is not sent to the browser.
Debugging I see that:
org.springframework.session.web.http.CookieHttpSessionStrategy.onNewSession() tries to write the cookie:
this.cookieSerializer
.writeCookieValue(new CookieValue(request, response, cookieValue));
org.springframework.session.web.http.DefaultCookieSerializer.writeCookieValue() sets the cookie in the response:
response.addCookie(sessionCookie);
The cookie isn't actually written. The underlying response object is org.apache.catalina.core.ApplicationHttpResponse. Its addCookie() method is:
/**
* Disallow <code>addCookie()</code> calls on an included response.
* #param cookie The new cookie
*/
#Override
public void addCookie(Cookie cookie) {
if (!included)
((HttpServletResponse) getResponse()).addCookie(cookie);
}
The problem is that included attribute, which at some point is set true, preventing the cookie from being added.
This happens when the jsp (using tiles) is being serviced:
UPDATE:
This is the moment when the response is being marked as included (when standard.jsp tiles layout is inserting an attribute:
<tiles:insertAttribute name="header" ignore="false"/>
To work around this problem I ended up creating a filter to enforce the creation of the session.
As seen, the first call to the controller didn't add the cookie because during the Tiles-JSP rendering the response was already marked as included. What I do is forcing the creation of the session in the filter and redirecting asking the very same requestURI. This way, since the call doesn't involve a tiles rendering the cookie is created and can be used right away in the next calls.
#Bean
#ConditionalOnExpression("${sessionEnforcerFilter.enabled:true}")
public FilterRegistrationBean sessionEnforcerFilter(){
logger.info("Registering sessionEnforcerFilter");
FilterRegistrationBean frb = new FilterRegistrationBean();
frb.setName("sessionEnforcerFilter");
frb.setFilter(new SessionEnforcerFilter());
frb.setUrlPatterns(Arrays.asList(new String[]{"/*"}));
return frb;
}
public class SessionEnforcerFilter implements Filter{
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
if(httpServletRequest.getSession(false)==null){
logger.debug("sessionEnforcerFilter.doFilter () - Session is null - forcing its creation");
httpServletRequest.getSession();
String requestURI = httpServletRequest.getRequestURI();
logger.debug("sessionEnforcerFilter.doFilter () - Repeating request [{}]", requestURI);
httpServletResponse.sendRedirect(requestURI);
}else{
chain.doFilter(httpServletRequest, response);
}
}
#Override
public void destroy() {}
}
summary
Hold breakPoint in SessionRepositoryResponseWrapper.onResponseCommitted().
Check that the response object inside the SessionRepositoryRequestWrapper is a non-wrapped response. (included = false)
If it is a wrapped response object, make sure that the sessionRepositoryFilter comes first.
================
Spring-session is already handling the problem when 'DispatcherType.INCLUDE (included = true)'.
SessionRepositoryResponseWrapper.onResponseCommitted() is trying to addCookie to the original response object.
The sessionRepositoryFilter must be in the first position to wrap the original applicationHttpResponse passed by tomcat.
Problem Situation
The SessionRepositoryRequestWrapper receives the wrapped response and holds it.
When executing doInclude() in the servlet container, find the original reponse and wrap it with ApplicationHttpResponse (included = true).
Then, SetResponse (new wrapping response) to the innermost wrapper.
http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat.embed/tomcat-embed-core/8.0.24/org/apache/catalina/core/ApplicationDispatcher.java#ApplicationDispatcher.doInclude%28javax.servlet.ServletRequest%2Cjavax.servlet.ServletResponse%29
http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat.embed/tomcat-embed-core/8.0.24/org/apache/catalina/core/ApplicationDispatcher.java#ApplicationDispatcher.wrapResponse%28org.apache.catalina.core.ApplicationDispatcher.State%29
Spring-session does an addCookie on the response (expecting the original response) stored in SessionRepositoryResponseWrapper.onResponseCommitted(), but it can not because it is set to 'included = true'.

Store url when #Controller is accessed Spring MVC

Is there a way to access the URL resolved from a Spring MVC controller - e.g.
#RequestMapping("/{language}/news/{articleId}")
public String newsPage(...) {
}
Resolves to:
/en/news/63421
I'd like to store this with the session so I can keep a track of last place visited. The motivation here is if the page is secured the login filter will come into play and we have used
SavedRequestAwareAuthenticationSuccessHandler
to route users back to the page they were trying to access.
However if they are viewing unsecured pages and choose to log in using a form that drops down from the top of the screen (the page's menu bar) the 'last page' seems to be the login form so the success handler drops them back to the root context.
I'd like to intercept controller calls and store a single URL with the session, override SavedRequestAwareAuthenticationSuccessHandler to allow us to modify the RequestCache and then let Spring redirect on login success.
Ideally we'd like a generic way to do this across all controllers but not sure if there is a filter we can use to pick this up - filtering requests gets all sorts of noise like css, js, images and html fragment pages so we're hoping someone knows a way to do this just with the controllers themselves.
There are two questions:
1) obtain the url in a controller method
#RequestMapping("/{language}/news/{articleId}")
public String newsPage(..., HttpServletRequest request) {
String uri = request.getRequestUri();
...
}
If you need this very often then you can implement a HandlerMethodArgumentResolver. *See this answer https://stackoverflow.com/a/8769670/280244 for an example (it implements a HandlerMethodArgumentResolver for the current user, but you can easyly adapt it for urls)
2.) store the url for each request in the session
You can implement a Servlet Filter or Spring HandlerInterceptor, both get a HttpServletRequest (In a Servlet Filter you need to cast the ServletRequest to an HttpServletRequest first.
Then you can obtain the url and the Session httpServletRequest.getSession() and then store the url in the session.
public class MyFilter implements Filter {
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
//do nothing
}
#Override
public void doFilter(ServletRequest requ, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
if (requ instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) requ;
httpServletRequest.getSession().setAttribute(
"myFilter.LAST_URL",
httpServletRequest .getRequestURI());
}
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
To get the URL path you can use the HttpServletRequest - so for example you have:
www.mysite.com/en/news/63421
req.getPathInfo() = /en/news/63421
Storing it in the session though could cause problems if someone is to use your site with multiple tabs open.

Wicket + WAS: calling url causes a redirect to a wrong URL causing 404

Using Websphere Application Server + Wicket 1.6
I am having some issues with my mounted URLs.
When I invoke an url akin to: localhost:9080/danesCooking/pies/meat I see the following in the Chrome network tab:
localhost:9080/danesCooking/pies/meat status code 302
localhost:9080/danesCooking/meat?2 status code 404
So it seems the /pies/ portion gets lost. This behaviour does not occur when I deploy my application to Tomcat\JBoss\... .
Possibly relevant, pies itself is not a mounted page.
I've already looked through some of the wicket issues\forums and it seems most issues seem to be either resolved\conflicting answers\have to do with relative urls (fixed in 1.5.x).
Has anyone experienced this issue and still recalls how to resolve this?
Used WAS *Versions: 7 and 8.5* liberty.
This issue is actually outlined here;
https://issues.apache.org/jira/browse/WICKET-3258
My resolution to the issue in Wicket 6.9.1 was;
public class MyApplication extends WebApplication {
#Override
public Class<? extends WebPage> getHomePage() {
return MyHomePage.class;
}
/* *********************************************** */
// Resolve Websphere Relative URL "sendRedirect" Bug
#Override
protected WebResponse newWebResponse(WebRequest webRequest, HttpServletResponse httpServletResponse) {
return new FixedServletWebResponse((ServletWebRequest) webRequest, httpServletResponse);
}
/**
* Websphere incorrectly handles relative redirect pages when "HttpServletResponse.sendRedirect(url)" is called.
*
* This small fix ensures that Websphere is only ever provided with absolute URLs so that this issue never occurs.
*/
private static class FixedServletWebResponse extends ServletWebResponse {
private final ServletWebRequest webRequest;
protected FixedServletWebResponse(ServletWebRequest webRequest, HttpServletResponse httpServletResponse) {
super(webRequest, httpServletResponse);
this.webRequest = webRequest;
}
#Override
public String encodeRedirectURL(CharSequence url) {
Url relativeUrl = Url.parse(url);
return new UrlRenderer(webRequest).renderFullUrl(relativeUrl);
}
}
/* *********************************************** */
}
You can also solve that using anonymous class as described in the Configuring and Deploying Open Source with WebSphere Application Server Liberty Profile chapter 5.2.4. (Tested with WLP 8.5.5.3 and Wicket 6.8.0)
Override the following method in your WebApplication class:
#Override
protected WebResponse newWebResponse(final WebRequest webRequest, final
HttpServletResponse httpServletResponse)
{
return new ServletWebResponse((ServletWebRequest) webRequest,
httpServletResponse)
{
#Override
public String encodeRedirectURL(final CharSequence relativeURL)
{
return new UrlRenderer(webRequest).renderFullUrl(Url.parse(relativeURL));
}
};
}
UPDATE
Other solution, instead of code change, is to set the following property in the webcontainer (works in 8.5.5.3):
<webContainer com.ibm.ws.webcontainer.redirectwithpathinfo="true" />

Resources