April 11, 2020
Estimated Post Reading Time ~

Servlet Filter in OSGi enabled web application

Servlet filters are an important component of web application and it can be used in different scenarios, e.g.:

1) Authenticating requests (ServletRequest) before delegating them to your actual business class (Servlet/Controller).
2) Formatting request (ServletRequest) and response data (ServletResponse)
3) Global/Application level Error handling

There are many more uses cases where we can use Servlet filters.
A traditional web application is composed of a couple of Business classes & data (model), JSP (views), controllers/servlets, and few other resources like css, images, etc. All these components are packaged as a WAR file and we deploy it on a web/application server. To add/configure a servlet filter in traditional web application we user web.xml configuration file and tag as:

 
  springSecurityFilterChain
  org.springframework.web.filter.DelegatingFilterProxy
 
     springSecurityFilterChain
     /*
     REQUEST
     ERROR

An OSGi web application is different as compared to traditional web applications. Below are a few major differences:

1) Traditional web applications are deployed on the web/application server and run under a servlet container whereas OSGi web application is deployed in an OSGi container that has servlet containers as an OSGi HTTP service.
2) In traditional web application required libraries/jars are part of the WAR file whereas in OSGi web application jar files are installed as OSGi bundles in OSGi container.
3) In traditional web application Filter are configured using web application whereas in OSGi web application Filter are configured/registered using OSGi HTTP service.

As I mentioned, in OSGi web application we don’t configure filters in web.xml file rather we need to register it through OSGi HTTP service, there are two different ways to do this:

1) Create a Filter and register it as an OSGi service into OSGi HTTP service (ExtHttpService)
2) Create a Filter and make it as an OSGi component (Whiteboard method)
In this post, we are going to create a simple filter to encode all request parameters using UTF-8 encoding before passing that request to a servlet/controller. We have used this concept in Adobe/Day CQ5 that uses Apache Felix as an OSGi framework for managing OSGi components and services.

1) Create a Filter and register it to OSGi HTTP service
a) Create a class that implements javax.servlet.Filter and add your logic to doFilter() method. Also, if required implement the init() and destroy() to do some initialization and cleanup activities.


public class CharacterEncodingFilter implements Filter {
 private String encoding = "UTF-8";
 private Boolean forceEncoding = true;
 public void init(FilterConfig filterConfig) throws ServletException {
  try {
   String encoding = filterConfig.getInitParameter("init.charencoding.filter.encoding");
   if(encoding != null && encoding.trim().length() >0) {
    this.encoding = encoding;

   }

   if(filterConfig.getInitParameter("init.charencoding.filter.forceencoding") != null) {
    Boolean forceEncoding = Boolean.parseBoolean(
      filterConfig.getInitParameter("init.charencoding.filter.forceencoding"));
    this.forceEncoding = forceEncoding;

   }
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }

 public void doFilter(ServletRequest request, ServletResponse response, 
   FilterChain chain) throws IOException, ServletException {
  ExtraParamWrappedRequest reqwrapper = new ExtraParamWrappedRequest((HttpServletRequest) request, null);

  if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
   Map additionalParams = new HashMap();
   additionalParams.put("_charset_", new String[]{encoding});
   reqwrapper = new ExtraParamWrappedRequest((HttpServletRequest) request, additionalParams);
   reqwrapper.setCharacterEncoding(this.encoding);

   if (this.forceEncoding) {
    response.setCharacterEncoding(this.encoding);
   }

  }

  chain.doFilter(reqwrapper, response);
 }

 public void destroy() {
  // TODO Auto-generated method stub
 }

 class ExtraParamWrappedRequest extends HttpServletRequestWrapper {
  private final Map modifiableParameters;
  private Map allParameters = null;

  public ExtraParamWrappedRequest(final HttpServletRequest request, final Map additionalParams) {
   super(request);
   modifiableParameters = new TreeMap();

   if(additionalParams != null) {
    modifiableParameters.putAll(additionalParams);
   }

  }

  @Override
  public String getParameter(final String name) {
   String strings = getRequest().getParameter(name);

   if (strings != null)  {
    return strings;
   }
   return null;
  }

  @SuppressWarnings("unchecked")
  @Override
  public Map getParameterMap() {
   if (allParameters == null) {
    allParameters = new TreeMap();
    allParameters.putAll(super.getParameterMap());
    allParameters.putAll(modifiableParameters);
   }
   return Collections.unmodifiableMap(allParameters);
  }

  @Override
  public Enumeration getParameterNames() {
   return Collections.enumeration(getParameterMap().keySet());

  }

  @Override
  public String[] getParameterValues(final String name) {
   return getParameterMap().get(name);
  }

 }
}

b) Every OSGi bundle has an Activator class, we need to register the filter that we have created in step (a) to OSGi HTTP service in our Activator class by getting the reference of HTTP service (ExtHttpService).

public class Activator implements BundleActivator {
 private static final Logger log = LoggerFactory.getLogger(Activator.class);

 public void start(BundleContext context) throws Exception {
  ServiceReference sRef = context.getServiceReference(ExtHttpService.class.getName());

  if (sRef != null) {
   Dictionary properties = new Hashtable();
   properties.put("service.pid", CharacterEncodingFilter.class.getName());
   properties.put("init.charencoding.filter.encoding", "UTF-8");
   properties.put("init.charencoding.filter.forceencoding", "true");
   ExtHttpService service = (ExtHttpService) context.getService(sRef);
   service.registerFilter(new CharacterEncodingFilter(), "/.*", properties, 0, null);
  }
 }

 public void stop(BundleContext context) throws Exception {
  System.out.println(context.getBundle().getSymbolicName() + " stopped");
 }

}

2) Create a Filter and make it as an OSGi component
The second method of registering a filter to OSGi container is, creating it as an OSGi component/service. Below are the steps that we need to follow:

a) Create a class that implements javax.servlet.Filter and add your logic to doFilter() method. Also, if required implement the init() and destroy() to do some initialization and cleanup activities. Basically, we can use the same class (CharacterEncodingFilter) that we have created above, the only difference we have in this method is the way of registering a filter.

b) Get hold of OSGi bundle context and register filter as a service as shown below:



public class Activator implements BundleActivator {
 private ServiceRegistration registration;
 public void start(BundleContext context) throws Exception {
  Hashtable props = new Hashtable();
  props.put("pattern", /.*");
    props.put("init.message", "Character encoding filter!");
  props.put("service.ranking", "1");
  this.registration = context.registerService(Filter.class.getName(), new CharacterEncodingFilter (), props);
 }

 public void stop(BundleContext context) throws Exception {
  this.registration.unregister();
 }

}

We have seen two different ways of registering filter for a web application that is running under OSGi container so, what’s the difference between these two methods?

An OSGi container may contain multiple OSGi web application and each web application runs under its own bundle context. HTTP service is the service that manages HTTP requests/responses for all web applications deployed under an OSGi container. The first method registers filter at the HTTP service level (global) and is applied to all request/response across all web application deployed in an OSGi container whereas the second method registers a filter at bundle (individual web application) level and gets only executed only when a request is for that particular application/bundle. When we have multiple filters, they can be positioned based on need (which one to execute first and after that and so on) at HTTP service (fourth argument of service.registerFilter method) and individual application bundle level (service.ranking property).
I hope this will help you to understand how Servlets filters works in OSGi enabled web applications.

References:
http://felix.apache.org/site/apache-felix-http-service.html
http://www.eclipse.org/equinox/server/


Source: http://suryakand-shinde.blogspot.com/2012/07/servlet-filter-in-osgi-enabled-web.html


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.