My application is visited from different countries and I am using a common servlet Filter (MyFilter.java) to control all requests. Is it possible to redirect to other Servlets based on country's visitor?
Currently my web.xml configuration is below
<filter>
<filter-name>myfilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myfilter</filter-name>
<url-pattern>*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
Suppose if user visits from USA I want to redirect it to US_Filter.java , or if user visits from Australia I want to redirect it to AU_Filter.java, or if User visits from UK I want to redirect it to UK_Filter.java. Is this possible from web.xml?
I am thinking of making Country wise configuration in web.xml like
country=US (US_Filter)
country=AU (AU_Filter)
country=UK (UK_Filter)
country=None (MyFilter)
But I don't know how?
I require this because of different behaviour we perform based on countries for example their mobile no validation, managing users subscription services, etc.
Please provide me suggestions.
Thanks,
I think it's not possible to use your web.xml for this. However, you can accomplish what you want by coding your MyFilter class carefully, so that you don't need to modify it when adding a new country.
I see you're using Spring. That's good news, since MyFilter is actually a bean managed by Spring. This means that other beans might be injected to it. My suggestion is that you have one bean per country and one main filter that would be in charged of delegating to the right country bean.
Firstly, let your web.xml as it is now:
<filter>
<filter-name>myfilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myfilter</filter-name>
<url-pattern>*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
This will let the bean named myfilter be invoked for every request.
Then, implement MyFilter in such a way that it's country-agnostic, so that it doesn't need to be modified when a country is added or removed:
#Component("myfilter")
public class MyFilter implements Filter {
public static final String DEFAULT = "default";
public static final String SUFFIX = "_Filter";
// Autowire all beans that implement CountryFilter, mapped by bean name
#Autowired
private Map<String, CountryFilter> filters;
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Get country code from request
Locale locale = request.getLocale();
String countryCode = locale.getCountry().toUpperCase();
// Format key to gey country-specific filter
String key = countryCode + SUFFIX;
// If bean doesn't exist for request country...
if (!this.filters.containsKey(key)) {
// ..fallback to default filter
key = DEFAULT + SUFFIX;
}
// Get filter for country
CountryFilter filter = this.filters.get(key);
// Delegate to actual country (or default) filter
boolean countinueChain = filter.doFilterForCountry(request, response);
if (continueChain) {
chain.doFilter(request, response);
}
}
#Override
public void init(FilterConfig config) throws ServletException {
}
#Override
public void destroy() {
}
}
This class is generic enough. You wouldn't need to change it when adding or removing a country. The trick is to use Spring autowiring behavior for collections. If you autowire a Map<String, T>, then Spring will populate this map with all instances of beans of class T, being keys equal to the bean names and values the corresponding bean instances.
Then, you'd need the CountryFilter interface:
public interface CountryFilter {
boolean doFilterForCountry(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
You'd need to implement the CountryFilter for each country, letting it be a Spring bean whose name matches the pattern CC_Filter, where CC stands for the 2-digit ISO country code. For example, for US you might have:
#Component("US" + MyFilter.SUFFIX)
public class UsFilter implements CountryFilter {
#Override
public boolean doFilterForCountry(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// TODO Handle US specifics here
// VERY IMPORTANT: you might want to let the chain continue...
return true;
// ...or redirect to US page
// ((HttpServletResponse) response).sendRedirect("US-url");
// return false;
// ONLY ONE of the options!
}
}
And for the UK:
#Component("UK" + MyFilter.SUFFIX)
public class UkFilter implements CountryFilter {
#Override
public boolean doFilterForCountry(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// TODO Handle UK specifics here
// VERY IMPORTANT: you might want to let the chain continue...
return true;
// ...or redirect to UK page
// ((HttpServletResponse) response).sendRedirect("UK-url");
// return false;
// ONLY ONE of the options!
}
}
Same for other countries.
Finally, it might happen that you have no implementation for a given country. In this case, you might want to have a default filter as a fallback case:
#Component(MyFilter.DEFAULT + MyFilter.SUFFIX)
public class DefaultFilter implements CountryFilter {
#Override
public boolean doFilterForCountry(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// TODO Handle DEFAULT specifics here
// VERY IMPORTANT: you might want to let the chain continue...
return true;
// ...or redirect to DEFAULT page
// ((HttpServletResponse) response).sendRedirect("DEFAULT-url");
// return false;
// ONLY ONE of the options!
}
}
Hope this helps you solve your problem. I believe it's a quite flexible approach, it even has a fallback case. To add a new country, all you'd need to do is implement a new CountryFilter.
http://examples.javacodegeeks.com/core-java/util/locale/java-util-locale-example/
please go through this site!!
Using that site i was suggesting you to add another filter so that it check for locale & redirect to desired servlet.
Related
I'm wondering if there is any pattern or implementation that allows me to enable/disable all the controllers at once of a given spring boot application by using a simple boolean variable that was provided by another feature flags service.
I'm thinking of putting a conditional check in each of the controller paths but it's a really bad way of doing it.
Thanks in advance
You can define custom Filter and register that. Sample Filter code at.
public class RequestAllowDenyFilter implements Filter {
private boolean isAllowed = true;
public RequestAllowDenyFilter(boolean isAllowed) {
this.isAllowed = isAllowed;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (isAllowed) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
}
}
}
Then when you register filter you need to pass do you want to allow/deny request.
#Value("${request.enabled:true}")
private boolean isEnabled;
#Bean
public FilterRegistrationBean<RequestAllowDenyFilter> loggingFilter() {
FilterRegistrationBean<RequestAllowDenyFilter> registrationBean
= new FilterRegistrationBean<>();
registrationBean.setFilter(new RequestAllowDenyFilter(isEnabled));
registrationBean.addUrlPatterns("/**");
registrationBean.setOrder(0);
return registrationBean;
}
You need to define request.enabled in application.properties file. Either true/false based on what you want to do.
I am trying to log (just to console write now for simplicity sake) the final rendered HTML that will be returned by the HttpServletResponse. (i.e. the body) To this end, I am using the HandlerInterceptorAdapter from Spring MVC like so:
public class VxmlResponseInterceptor extends HandlerInterceptorAdapter {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println(response.toString());
}
}
This works as expected and I see the HTTP response headers in the console. My question is if there is a relatively simple way to log the entire response body (i.e. final rendered HTML) to the console without having to resort to doing jumping jacks with PrintWriters, OutputStream's and the like.
Thanks in advance.
This would be better done using a Servlet Filter rather than a Spring HandlerInterceptor, for the reason that a Filter is allowed to substitute the request and/or response objects, and you could use this mechanism to substitute the response with a wrapper which logs the response output.
This would involve writing a subclass of HttpServletResponseWrapper, overriding getOutputStream (and possibly also getWriter()). These methods would return OutputStream/PrintWriter implementations that siphon off the response stream into a log, in addition to sending to its original destination. An easy way to do this is using TeeOutputStream from Apache Commons IO, but it's not hard to implement yourself.
Here's an example of the sort of thing you could do, making use of Spring's GenericFilterBean and DelegatingServletResponseStream, as well as TeeOutputStream, to make things easier:
public class ResponseLoggingFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse responseWrapper = loggingResponseWrapper((HttpServletResponse) response);
filterChain.doFilter(request, responseWrapper);
}
private HttpServletResponse loggingResponseWrapper(HttpServletResponse response) {
return new HttpServletResponseWrapper(response) {
#Override
public ServletOutputStream getOutputStream() throws IOException {
return new DelegatingServletOutputStream(
new TeeOutputStream(super.getOutputStream(), loggingOutputStream())
);
}
};
}
private OutputStream loggingOutputStream() {
return System.out;
}
}
This logs everything to STDOUT. If you want to log to a file, it'll get a big more complex, what with making sure the streams get closed and so on, but the principle remains the same.
If you're using (or considering) logback as your logging framework, there is a nice servlet filter already available that does exactly that. Checkout the TeeFilter chapter in the documentation.
I've been looking for a way to log full HTTP Request/Response for a while and discovered it has been solved for me in the Tomcat 7 RequestDumperFilter. It works as advertised from a Tomcat 7 container. If you want to use it in Jetty, the class works fine stand-alone or, as I did, copied and adapted to the specific needs of my environment.
I made a small library spring-mvc-logger available via maven central.
Add to pom.xml:
<dependency>
<groupId>com.github.isrsal</groupId>
<artifactId>spring-mvc-logger</artifactId>
<version>0.2</version>
</dependency>
Add to web.xml:
<filter>
<filter-name>loggingFilter</filter-name>
<filter-class>com.github.isrsal.logging.LoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Add to log4j.xml:
<logger name="com.github.isrsal.logging.LoggingFilter">
<level value="DEBUG"/>
</logger>
the code pasted below works with my tests and can be downloaded from my github project, sharing after applying a solution based on that on a production project
#Configuration
public class LoggingFilter extends GenericFilterBean {
/**
* It's important that you actually register your filter this way rather then just annotating it
* as #Component as you need to be able to set for which "DispatcherType"s to enable the filter
* (see point *1*)
*
* #return
*/
#Bean
public FilterRegistrationBean<LoggingFilter> initFilter() {
FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoggingFilter());
// *1* make sure you sett all dispatcher types if you want the filter to log upon
registrationBean.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
// *2* this should put your filter above any other filter
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registrationBean;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ContentCachingRequestWrapper wreq =
new ContentCachingRequestWrapper(
(HttpServletRequest) request);
ContentCachingResponseWrapper wres =
new ContentCachingResponseWrapper(
(HttpServletResponse) response);
try {
// let it be ...
chain.doFilter(wreq, wres);
// makes sure that the input is read (e.g. in 404 it may not be)
while (wreq.getInputStream().read() >= 0);
System.out.printf("=== REQUEST%n%s%n=== end request%n",
new String(wreq.getContentAsByteArray()));
// Do whatever logging you wish here, in this case I'm writing request
// and response to system out which is probably not what you wish to do
System.out.printf("=== RESPONSE%n%s%n=== end response%n",
new String(wres.getContentAsByteArray()));
// this is specific of the "ContentCachingResponseWrapper" we are relying on,
// make sure you call it after you read the content from the response
wres.copyBodyToResponse();
// One more point, in case of redirect this will be called twice! beware to handle that
// somewhat
} catch (Throwable t) {
// Do whatever logging you whish here, too
// here you should also be logging the error!!!
throw t;
}
}
}
in our application we are creating request filters which should not be hit for some urls. We want be able to exclude urls like spring do witch url patterns, e.g.:
// register filter in bean
FilterRegistrationBean filterBeanRegistration = new FilterRegistrationBean();
filterBeanRegistration.setFilter(myFilter());
filterBeanRegistration.addInitParameter("excluded", "*/foo/**, */bar/**");
...
and new filter doesn't hit urls like domain:8080/aaa/foo/xxxx, domain:8080/bbb/bar/xxxx .
Can you tell me how to do that with spring classes or another simple way? Thank you in advice.
EDIT:
There is FilterBeanRegistration#addUrlPatterns(String... urls) method where I can specify urls, but there is no any format which tells which url should hit. For our purposes is better exclude some urls.
You can use org.springframework.web.filter.OncePerRequestFilter. It gets executed once every incoming request. You can override shouldNotFilter method to exclude the URLs you don't want the filter to run for. Example code:
public class MyFilter extends OncePerRequestFilter {
private static final String[] excludedEndpoints = new String[] {"*/foo/**, */bar/**"};
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return Arrays.stream(excludedEndpoints)
.anyMatch(e -> new AntPathMatcher().match(e, request.getServletPath()));
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// Filtering logic goes here. call below line on successful authentication.
filterChain.doFilter(request, response);
}
}
I have a class implementing a JAX-RS endpoint, as per below:
#Produces({MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED})
#Path("/site/")
public class ApiSiteResource extends AbstractContentResource {
...
#GET
#Path("/article/")
public Map<String, Object> getArticle (#Context HttpServletRequest request, #Context HttpServletResponse reponse, #BeanParam ApiParams params) {
//do stuff
}
#GET
#Path("/category/")
public Map<String, Object> getCategory (#Context HttpServletRequest request, #Context HttpServletResponse reponse, #BeanParam ApiParams params) {
//do stuff
}
What I need is to perform common processing (for example, capture analytics data) when any of the endponts of the above REST class is invoked, e.g., both for /site/article/ and /site/category/. I'm ideally looking for a solution that would be invoked at the end of the method execution, and ideally with least possible change to the existing methods code, so adding another method call at the end of the method is not the best option as that leads to too much code coupling. Ideally, I would like processing to be fired from an external class.
Is there a way how that could be done?
I am using a method of the Resource class that is annotated with the #Context Annotation and has a parameter that is injected from the context scope.
/**
* This method is called by JAX-RS for each request before
* the identified resource method is invoked, since it is
* annotated with the Context Annotation and carries a
* context-scope parameter which is injected.
*/
#Context
public void setServletContext( ServletContext servletContext ) {
...
}
(If you remove the ServletContext parameter, the automatic invocation on each resource call vanishes - at least in Jersey.)
Furthermore, you can put this method in a base class, say DefaultResourceImpl, which your Resource classes can extend, so you have this for all your Resource classes.
You can use JAX-RS Filters and Interceptors
For example there exist Request filters and response filters. You may do some stuff there:
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
public class PoweredByResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("X-Powered-By", "Jersey :-)");
}
}
Thank you all for your useful replies and comments.
Actually, capturing analytics was just half the story. In fact, I've also needed to add response headers.
So, I ended up implementing a filter as below:
public class ApiResourceHeadersFilter extends OncePerRequestFilter {
public ApiResourceHeadersFilter() {
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.setHeader("Access-Control-Allow-Headers", "accept");
response.setHeader("Access-Control-Allow-Methods", "GET OPTIONS");
filterChain.doFilter(request, response);
}
}
Plus added a mapping in the web.xml:
<filter>
<filter-name>ApiResourceHeadersFilter</filter-name>
<filter-class>com.workangel.eap.filters.ApiResourceHeadersFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ApiResourceHeadersFilter</filter-name>
<url-pattern>/api/site/*</url-pattern>
</filter-mapping>
Works like a charm; no messy code dependencies or modification. I'm sure I can extend it further should I need to collect analytics data as well.
I'd like the ability to modify/configure filters in a different way than web.xml. Here is a static configuration of 2 filters. I'd like the ability to have one filter statically configured and allow that filter to load additional filters. I just wanted to know if anyone knows of lib that already has this.
Using Servlet API 2.5
<web-app>
...
<filter>
<filter-name>MyFilter1</filter-name>
<filter-class>com.me.MyFilter1</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
<filter>
<filter-name>MyFilter2</filter-name>
<filter-class>com.me.MyFilter2</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
I've seen this done in Guice with GuiceFilter where the Filters are configured at runtime.
Just do the same job as the container already does. I.e. reinvent the wheel of the chain of responsibility design pattern as is under the covers been used by servlet filters.
public class GodFilter implements Filter {
private Map<Pattern, Filter> filters = new LinkedHashMap<Pattern, Filter>();
#Override
public void init(FilterConfig config) throws ServletException {
Filter1 filter1 = new Filter1();
filter1.init(config);
filters.put(new Pattern("/foo/*"), filter1);
Filter2 filter2 = new Filter2();
filter2.init(config);
filters.put(new Pattern("*.bar"), filter2);
// ...
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest hsr = (HttpServletRequest) request;
String path = hsr.getRequestURI().substring(hsr.getContextPath().length());
GodFilterChain godChain = new GodFilterChain(chain);
for (Entry<Pattern, Filter> entry : filters.entrySet()) {
if (entry.getKey().matches(path)) {
godChain.addFilter(entry.getValue());
}
}
godChain.doFilter(request, response);
}
#Override
public void destroy() {
for (Filter filter : filters.values()) {
filter.destroy();
}
}
}
with those little helper classes (which can if necessary be made private static nested classes of the above GodFilter):
public class Pattern {
private int position;
private String url;
public Pattern(String url) {
this.position = url.startsWith("*") ? 1
: url.endsWith("*") ? -1
: 0;
this.url = url.replaceAll("/?\\*", "");
}
public boolean matches(String path) {
return (position == -1) ? path.startsWith(url)
: (position == 1) ? path.endsWith(url)
: path.equals(url);
}
}
and
public class GodFilterChain implements FilterChain {
private FilterChain chain;
private List<Filter> filters = new ArrayList<Filter>();
private Iterator<Filter> iterator;
public GodFilterChain(FilterChain chain) {
this.chain = chain;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (iterator == null) {
iterator = filters.iterator();
}
if (iterator.hasNext()) {
iterator.next().doFilter(request, response, this);
} else {
chain.doFilter(request, response);
}
}
public void addFilter(Filter filter) {
if (iterator != null) {
throw new IllegalStateException();
}
filters.add(filter);
}
}
You could if necessary also feed a XML config file with all possible filters so that you end up with easier configuration. You could use reflection to create filters in init() of your GodFilter.
Oh nevermind, that's what the web.xml and the container already is doing...
Servlet 3.0 has the #WebFilter annotation to define a filter. No need to declare it in web.xml anymore.
But loading a filter from a filter is not supported. You could implement it yourself: it's "just" the chain of responsibility pattern, but why would you?
It can be achieved in easy steps, even for pre-3.0 Servlet spec:
Add a filter containing a static & ordered collection of Classes (chain).
Map the filter to intercept every traffic.
Manipulate the order & existence of your helper classes (those will be called privately by your filter upon interception of traffic) in the chain.
Ref: Xstream uses same kind of pattern for Serializer, well not with Servlet/Filter though. :)
I personally like the #WebFilter annotation to register servlet filters.
But another solution is to add the filter at runtime using the ServletContext's addFilter function.
Implement ServletContextListener; something like:
public class MyContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent ce) {
ServletContext servletContext = ce.getServletContext();
// you can even conditionally add this
servletContext.addFilter("My filter 1", MyFilter1.class)
.addMappingForUrlPatterns(allOf(DispatcherType.class), false, "/*");
}
}
Register listener:
<listener>
<listener-class>com.me.MyContextListener</listener-class>
</listener>
And of course you need to implement a Filter. But in your question you already refer to an example filter 'MyFilter1'.