Intercepting JNDI Filters

This article presents a simple filtering framework to "intercept" JNDI operations and objects in a non-intrusive way (without code changes or the overhead of AOP systems).

Suppose you have an existing J2EE application with EJB's, RMI objects, JMS destinations and other objects bound into a JNDI registry. During the course of the project schedule, you need to make significant changes to the underlying architecture, re-define business processes and/or need to identify transactional/performance problems. Without a proper framework in place, it can be difficult to make "non-intrusive" changes to an existing system without rippling side effects.

This article presents a simple filtering framework to "intercept" JNDI operations and objects in a non-intrusive way (without code changes or the overhead of AOP systems). You can "peek-into the JNDI subsystem" and fully control the behavior of an application to support:

  •  Interception of JNDI operations (such as lookup, bind, rebind, etc).
  • Interception of JNDI-bound object invocations before the object is called.
  • Examination of the parameters before a JNDI-bound object is called.
  • Modification of the parameters.
  • Modification of the return values.
  • Interception of JNDI-bound object invocations after the object is called.
  • Chaining of requests and responses.

The J2EE specification provides a filtering API for Servlets. Unfortunately, a similar API does not exist for other J2EE components such as:

  • EJBs,
  • RMI objects,
  • JMS destinations
  • Other JNDI-bound objects (i.e., javax.sql.DataSource, transaction managers, foreign JNDI objects from other providers, etc).

Benefits and Usage

In general, JNDI filters can be used as an advanced J2EE feature primarily intended for situations where the developer cannot change the coding of an existing resource and needs to modify the code to change the behavior of the resource. Generally, it is more efficient to modify the code to change the behavior of the resource itself rather than using filters to modify the resource. But, in most cases, that is not possible, and changes need to be "loosely-coupled" around an existing architecture.

The following benefits are gained in JNDI filtering:

  • Adaptability - Allows the re-configuration of business processes and the ability to 're-route' requests to other services based on service-level agreements (SLAs).
  • Extensibility - Allows pre/post processing for logging, auditing, security and custom logic without breaking existing interface contracts.
  • Flexibility - Easily reconfigure and chain filters on a dynamic basis.
  • Modularization - Encapsulation of application-processing logic into Java code can define modular units easily and can be added to and removed from a request/response chain.

Here are some common filter usage patterns:

Filter Type

Description

Logging and Auditing

Messages can be logged before and after the execution of the target object. Auditing and statistics can be performed to record invocation time, response time, etc.

JDBC and DataSource

Print out SQL queries and timings on javax.sql.DataSource objects and java.sql.Connection objects to get a detailed view of what is going on with database access. Furthermore, this can be used in conjunction with the Caching filter to have a proxy between the app server and database for performance improvements.

Security

Provides an 'app-server neutral' approach for authentication and authorization.

Caching

Cache data without invoking the service.

Transaction Management

Display detailed information about the current transaction (such as XID, TX statuses, etc). Fully control the demarcation of transactions such as selectively suspending or starting a new transaction.

Course Grained

Redefine business processes dynamically.

Perform dynamic rerouting and change the behavior of the service invocation by breaking down logical steps across multiple services based on service level agreements ( SLA).

HA (High Availability)

Check for the existence and availability of an object invocation service. Asynchronous communication can be performed if a service is unavailable.

Request/Response Modification

Manipulating the request/response.

Real-World Example

Let's start with a real-world example. Suppose you are building a J2EE banking system and have the following code embedded within your application:

InitialContext ic = new 
  InitialContext();

AccountHome home = (AccountHome) 
 ic.lookup("ejb20-beanManaged-AccountHome");

Account account = home.findByPrimaryKey("12345");

account.withdraw(200);

This code simply looks up an 'Account' EJB from JNDI and performs a withdraw operation.

Now, assume that the code behind the bank account EJB is very complicated and fragile. The following new features need to be added to the system (because the current code doesn't support them):

  • Check the account balance before withdrawing funds.
  • Perform a security fraud check when withdrawing funds (auditing and logging).
  • Measure how long the withdraw operation takes to execute and capture statistical information on user input.
  • Measure SQL performance bottlenecks and audit SQL statements and timings.
  • Perform asynchronous, high-availability persistence if the Account EJB is unavailable due to system outages.

JNDI filters allow the code above to be untouched while performing the new requirements in a highly modular, decoupled manner.

How does this work? Well, the same code:

InitialContext ic = new 
  InitialContext();

AccountHome home = (AccountHome) 
 ic.lookup("ejb20-beanManaged-AccountHome");

Account account = home.findByPrimaryKey("12345");

account.withdraw(200);

is left untouched. The following happens under the covers with JNDI filters:


Figure 1 – Client Interactions

  • When the new InitialContext() operation is invoked, an instance of a limaye.filter.impl.DefaultInitialContextFactory is used. This is set asA system property in the JVM.
  • Dynamic proxies are created when JNDI lookup operations are performed. Hence, JNDI filters are invoked (potentially in a chain) for pre/post processing.

Introducing JNDI Filters

JNDI filters are Java classes that can be invoked in response to a request for a resource in an intercepted JNDI object. They are very similar to Servlet filters but are used on the server-side.

Resources can include any JNDI-bound object such as EJBs, RMI objects, etc. A filter intercepts the request and can examine and modify the response and request objects or execute other tasks.

A filter intercepts a request for a specific named resource or a group of resources (based on a JNDI name, target class and target return value classes) and executes the code in the filter. For each resource or group of resources, you can specify a single filter or multiple filters that are invoked in a specific order, called a chain.

When a filter intercepts a request, it has access to the limaye.filter.api.Request and limaye.filter.api.Response objects that provide access to the proxy object, its method, arguments, naming context. and a limaye.filter.api.FilterChain object. The FilterChain object contains a list of filters that can be invoked sequentially. When a filter has completed its work, the filter can call the next filter in the chain, block the request, throw an exception, or invoke the originally requested resource.

After the original resource is invoked, control is passed back to the filter at the bottom of the list in the chain. This filter can then examine and modify the request parameters and return value, block the request, throw an exception, or invoke the next filter up from the bottom of the chain. This process continues in reverse order up through the chain of filters.


Figure 2 – High Level API Class Diagrams

Writing a Filter Class

To write a filter class, implement the limaye.filter.api.Filter interface. You must implement the following methods of this interface:

  • init()
  • doFilter()

You use the doFilter() method to examine and modify the request and response objects, perform other tasks such as logging, invoke the next filter in the chain, or block further processing.

Several other methods are available on the FilterConfig object for accessing the name of the filter, the java.naming.Context and the filter's initialization attributes. To access the next item in the chain (either another filter or the original resource, if that is the next item in the chain), call the FilterChain.doFilter() method.

Configuring Filters

You can configure filters in the filter-config.xml file that is part of the system classpath. You specify the filter and then map the filter to a JNDI-Name, target and target-return value classes.

To configure a filter:

  • Open the filter-config.xml file. The filter-config.xml file is located in this distribution.
  • Add a filter declaration. The <filter> element declares a filter, defines a name for the filter, and specifies the Java class that executes the filter. The <filter> element must directly be below the <filter-config> root element. For example:
    <filter-config>
    <filter>
          <filter-name>LogFilter</filter-name>
          <filter-class>
               examples.LogFilter
          </filter-class>
          <description>
            Performs simple logging of the class, 
      methods and interfaces
           </description>
          <display-name>Log Filter</display-name>
    </filter>
    </filter-config>
    
    The description, and display-name elements are optional.
  • Specify one or more initialization attributes inside a <filter> element. For example:
    <filter>
          <filter-name>LogFilter</filter-name>
          <filter-class>
               examples.LogFilter
          </filter-class>
          <description>
            Performs simple logging of the class, 
      methods and interfaces
           </description>
          <display-name>Log Filter</display-name>
          <init-param>
             <param-name>TestKey1</param-name>
             <param-value>TestValue</param-value>
          </init-param> 
    </filter>
  • Add filter mappings. The <filter-mapping> element specifies which filter to execute based on a JNDI name, target interface name and target return value interface names.

    The <filter-mapping> element must immediately follow the <filter> element(s).

    NOTE : Wildcards can be used for the jndi-name, target-class, methods or target-return-class-list for pattern matching. If a match fails, a Class.isAssignable check is performed.
Your Filter class can read the initialization attributes using the FilterConfig.getInitParameter() or FilterConfig.getInitParameters() methods.

Example:

Suppose you have the following code in your application:

InitialContext ic = new 
  InitialContext();

Object obj = 
 ic.lookup("ejb20-beanManaged-AccountHome");

Account account = (Account) 
 PortableRemoteObject.narrow(obj

 Account.class);

 account.withdraw(200);

You would define the filter-config.xml as the following:

<filter-mapping>
   <filter-name>LogFilter</filter-name>
   <jndi-name>ejb20-beanManaged-AccountHome</jndi-name>
   <target-class>
       examples.ejb20.basic.beanManaged.AccountHome
   </target-class>
   <target-return-value-class-list>
      <target-return-value-class>
           examples.ejb20.basic.beanManaged.Account
</target-return-value-class>
   </target-return-value-class-list>
  <description>
         test description
       </description>
          </filter-mapping>

The LogFilter class will look like the following:

public class LogFilter implements Filter {
   
 public void doFilter(Request request, 
  Response response, FilterChain chain) 
   throws FilterException {
  
  System.out.println("*** Start Log " + 
   Filter on method:"+
    request.getMethod());
  chain.doFilter(request, response);  
   System.out.println("*** End Log Filter **");
}
}

Here are some combinations that you can use in the filter-config.xml:

Jndi-name

Target-class

methods

Target-return-value-class-list

description

*

java.rmi.Remote

*

*

Proxies any remote object and all return values

*

javax.ejb.EJBHome

javax.ejb.EJBObject

Proxies all EJBs (home objects for the target-class and remote objects for the return values).

*

examples.ejb20.basic.beanManaged.*

*

examples.ejb20.basic.beanManaged.*

Proxies any object that falls in this package.

ejb20*

examples.ejb20.basic.beanManaged.AccountHome

*

examples.ejb20.basic.beanManaged.Account

Proxies any jndi-name that starts with ejb20 and matches the target and return value interfaces

  • To create a chain of filters, specify multiple filter mappings. For more information, see the next section, Configuring a chain of filters.

Configuring a Chain of Filters

This framework creates a chain of filters by creating a list of all the filter mappings that match an incoming request's JNDI name and/or target-class and target-return value interface combinations. The ordering of the list is determined by the following sequence:

  1. Filters where the filter-mapping element contains a jndi-name that matches the request are added to the chain in the order they appear in the filter-config.xml file.
  2. Filters where the filter-mapping element contains a filter-name that matches the request are added to the chain after the filters that match a pattern.
  3. The last item in the chain is always the originally requested resource.

In your filter class, use the FilterChain.doFilter() method to invoke the next item in the chain.

Specifying JNDI parameters

A delegate initial context factory must be specified for the framework to dynamically proxy requests. This is defined in the <initial-context-factory-class> element and is required.

In the case of Weblogic, it would be something like: weblogic.jndi.WLInitialContextFactory or for JBoss something like:

org.jnp.interfaces.NamingContextFactory

<jndi>
      <initial-context-factory-class>weblogic.jndi.WLInitialContextFactory</initial-context-factory-class>
   </jndi>

Environment Prerequisites

  • JDK 1.3 or above
  • JNDI-capable JVM
  • The jndi_filter.jar and filter-config.xml files must be in the system classpath.
  • A system property must be set to use the limaye.filter.impl.InitialContextFactory as the default IntitialContext context factory or the Context.INITIAL_CONTEXT_FACTORY property must be set to the limaye.filter.impl.InitialContextFactory when instantiating a new InitialContext.

How It Works

Sun Microsystems's JNDI specification allows for different InitialContextFactory implementations to be specified at runtime. The technique that I will present to you utilizes a custom JNDI factory with dynamic proxies.

Listing 1: DefaultInitialContextFactory.java

Some key points to note about this class:

  • Implements the javax.naming.spi.InitialContextFactory interface.
  • Uses the AbstractFactory pattern to "plug-in" a JNDI provider that delegates to a concrete JNDI provider.
  • "Bootstraps" using the following techniques:
    • Using a system property called java.naming.factory.inital=Limaye.filter.impl.DefaultInitialContextFactory
    • Passing it into the JNDI environment. For example
      Hashtable env = new Hashtable();
      env.put(InitialContextFactory.INITIAL_CONTEXT_FACTORY, 
        "Limaye.filter.impl.DefaultInitialContextFactory");
      Context ctx = new InitialContext(env);
      • Putting the entry java.naming.factory.inital=Limaye.filter.impl.DefaultInitialContextFactory in the jndi.properties that is located in the system classpath.
      • Creating a startup class when the JVM initializes that sets the System Property java.naming.factory.inital=Limaye.filter.impl.DefaultInitialContextFactory

        Note: It is not a best practice to "hardcode" the InitialContextFactory class into the code. Instead, it is better to use an external mechanism, such as a Properties file, to store the InitialContextFactory classname. Code like:
        Hashtable env = new Hashtable();
        env.put(InitialContextFactory.INITIAL_CONTEXT_FACTORY, 
          "weblogic.jndi.WLInitialContextFactory");
        Context ctx = new InitialContext(env);
        
        Exposes 'weblogic-specific' characteristics and can be harder to move to other app servers.
  • A dynamic proxy of the concrete Context implementation is created.
  • A helper class called Loader is used to:
    • Ensure that the delegate InitialContextFactory class and Context objects are properly initialized (due to a Weblogic timing issue at startup).
    • Retrieve the list of Filter objects from the XML configuration and store them into memory.

Listing 2: Creates dynamic proxies on the JNDI bound objects, its return values and invokes the filters.

Some key points to note about this class:

  • Intercepts "lookup" operations and creates dynamic proxies on the JNDI objects.
  • Invokes the matched filter and chains.
  • Creates dynamic proxies on the matched return values.

J2EE Servlet Filters vs. JNDI Filters

First and foremost, the major difference between J2EE Servlet filters and JNDI filters is that Servlet filters are used for web resources and JNDI filters are not. Here are some important points to note about these two technologies:

  • Servlet filters are part of the J2EE specification and are configurable via the web application container's deployment descriptors. JNDI filters are decoupled from a J2EE container.
  • Servlet filters and JNDI filters share a very similar API that supports chaining, regular expressions, etc for pre/post processing.
  • Interception occurs on the "client" side.

Servlet filters and JNDI filters can be used hand-in-hand by acting as a "traffic" copy to perform simple interceptions such as:

  • Server-side validation.
  • Server-side authentication/authorization.
  • Server-side logging, auditing and statistics.

Alternative Technologies

Aspect-oriented programming (AOP) offers several techniques to allow reusable code snippets, or "aspects" to be injected within "cross-cuts" of application code. Several AOP frameworks are available ranging from heavy-weight implementations to simple, reflection based systems.

This article demonstrates how a custom JNDI provider and dynamic proxies can be used to provide "non-intrusive" changes to applications. It can be categorized as a 'simple', lightweight AOP system that uses Java reflection and dynamic proxies without the use of complicated and proprietary AOP compiler techniques.

Biography

Bahar Limaye is a System Architect at The College Board. He has extensive experience is building distributed O-O systems. He can be reached at [email protected].

Resources

http://java.sun.com/products/jndi/docs.html
http://java.sun.com/j2se/1.3/docs/guide/reflection/proxy.html
http://java.sun.com/products/servlet/Filters.html
http://java.sun.com/j2ee/
http://java.sun.com/blueprints/patterns/InterceptingFilter.html

Listings

Listing 1  DefaultInitialContextFactory.java 

package limaye.filter.impl;

import java.lang.reflect.Proxy;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;

import limaye.filter.impl.config.DefaultFilterSet;

public class DefaultInitialContextFactory implements InitialContextFactory {
  
 public final static String DELEGATE_INITIAL_CONTEXT_FACTORY =
  "JNDI_FILTER.DELEGATE_INITIAL_CONTEXT_FACTORY";
 
 public DefaultInitialContextFactory() {
  Loader.initialize(this);
 }

 public Context getInitialContext(Hashtable environment) throws NamingException {
  try
  {              
   DefaultFilterSet filterSet = Loader.getFilterSet();
   String initialContextFactoryDelegate = filterSet.getInitialContextFactoryClassName();
   if (environment.get(DELEGATE_INITIAL_CONTEXT_FACTORY) != null) {
    initialContextFactoryDelegate = 
      (String) environment.get(DELEGATE_INITIAL_CONTEXT_FACTORY);
   }
   Class clazz = Class.forName(initialContextFactoryDelegate);
   javax.naming.spi.InitialContextFactory icf = 
          (javax.naming.spi.InitialContextFactory) clazz.newInstance();
         Context context = icf.getInitialContext(environment);
         if (Loader.getFilterSet() == null) {
       return context;
         }
         else
         {    
       Context ctx = (Context)Proxy.newProxyInstance(context.getClass().getClassLoader(),
            new Class[] { Context.class },
      new ProxyInvocationHandler(context));
          return ctx;
         }
  } catch (Throwable ignored) {}
  return null;
    }
}



Listing 2  ProxyInvocationHandler.java 

package limaye.filter.impl;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.naming.Context;

import limaye.filter.api.FilterException;
import limaye.filter.api.Response;
import limaye.filter.impl.config.DefaultFilterSet;
import limaye.filter.impl.config.DefaultFilterSetEntry;

class ProxyInvocationHandler implements InvocationHandler, Serializable {
    
 private final static String LOOKUP_METHOD_NAME = "lookup";
 
 private final DefaultFilterSetEntry filterSetEntry;
 private final Context context;
 private final Object targetObject;
      
 ProxyInvocationHandler(Context context) {
  this(null, context, context);
 }
 
    ProxyInvocationHandler(DefaultFilterSetEntry filterSetEntry,
              Context context,
     Object targetObject) {
               
     if (context == null) {
      throw new IllegalArgumentException("Context is null.");
     }
     
     if (targetObject == null) {
      throw new IllegalArgumentException("Object is null.");
     }
                   
     this.filterSetEntry = filterSetEntry;
     this.context = context;
     this.targetObject     = targetObject;
 
    }

    public Object invoke(Object proxy, 
        Method method, 
     Object[] args) 
     throws Throwable {
        
     if(Context.class.isAssignableFrom(targetObject.getClass())) {      
      return createContextProxy(method, args);
     }
          
     Response response = null;
     try
  {
      response = doFilter(targetObject, method, args);
      return createProxyOnResponse(response);
  } 
     finally
  {
     }
    }
    
 private Response doFilter(Object object, Method method, Object[] args) throws Throwable, 
   FilterException {
  ClassLoader classLoader = getClassLoader(object.getClass());
     DefaultRequest request = createRequest(object, method, args);
     DefaultResponse response = createResponse();
     DefaultFilterChain chain = createFilterChain(object,
        classLoader, 
           filterSetEntry,
     context);
     try
  {
      String[] methodNames = filterSetEntry.getKey().getMethodNames();
      System.out.println("Method names are: " + methodNames);
      System.out.println("Real Method name is: " + method.getName());
      for (int i=0; i < methodNames.length; i++) {
       if (WildcardPatternMatcher.matches(methodNames[i], method.getName(), true)) {
        System.out.println("*** Matches!");
        chain.doFilter(request, response);
        break;
       }
      }
  } catch (FilterException fe) {
   if (fe.getCause() instanceof InvocationTargetException) {
    throw ((InvocationTargetException)fe.getCause()).getTargetException();
   }
   else
   {
    throw fe;
   }
  }
  finally
  {
  }
  return response;
 }

 /**
  * @param response
  * @return
  * @throws IllegalArgumentException
  */
 private Object createProxyOnResponse(Response response) throws IllegalArgumentException, 
   ClassNotFoundException {
  
  if (response == null) {
   throw new IllegalArgumentException("Response is null.");
  }
  Object returnValue = response.getReturnValue();
  if(returnValue != null) {
   System.out.println("Return Value Class Name is: " + returnValue.getClass().getName());
   String[] targetReturnValueClassNames = filterSetEntry.getTargetReturnValueClassNames();
   Class[] returnValueInterfaces = returnValue.getClass().getInterfaces();
   
   boolean doInterfacesMatch = doInterfacesMatch(targetReturnValueClassNames, returnValue);
   if (doInterfacesMatch) {
    returnValue =  createNewProxyInstance(context, returnValue);
   }
   
  }
  return returnValue;
 }
 
 private Object createContextProxy(Method method, Object[] arguments) throws Throwable { 
  Object returnValue = null;
    
  DefaultFilterSet filterSet = getFilterSet();
  if (filterSet == null) {
   return null;
  }
  
  try
  {
   returnValue = method.invoke(targetObject, arguments); 
   if (returnValue == null) { return null; }
      
  } catch(InvocationTargetException ite) {
   throw ite.getTargetException();
  }
  
  if(method.getName().equals(LOOKUP_METHOD_NAME) && arguments[0] instanceof String) {      
   String jndiName = (String)arguments[0];
   DefaultFilterSetEntry filterSetEntry = getFilterSetEntry(jndiName, returnValue);
   
   if(filterSetEntry != null) {
    returnValue = createNewProxyInstance(context, returnValue, filterSetEntry);  
   }
  }
  return returnValue;
 } 
 
 private Object createNewProxyInstance(Context context, Object returnValue) throws 
   IllegalArgumentException, ClassNotFoundException { 
  return createNewProxyInstance(context, returnValue, this.filterSetEntry);
 }
 
 private Object createNewProxyInstance(Context context, Object returnValue, 
   DefaultFilterSetEntry entry) throws IllegalArgumentException, ClassNotFoundException {
  ClassLoader classLoader = getClassLoader(returnValue.getClass());
      
  returnValue = Proxy.newProxyInstance(returnValue.getClass().getClassLoader(),
    returnValue.getClass().getInterfaces(),
    new ProxyInvocationHandler(entry,
      context,
      returnValue));
  return returnValue;
 }
  
 private ClassLoader getClassLoader(Class clazz) {
  ClassLoader classLoader = null;
  if (clazz == null) {
   classLoader = Thread.currentThread().getContextClassLoader();
  }
  else
  {
   classLoader = clazz.getClassLoader();
   if (classLoader == null) {
    classLoader = Thread.currentThread().getContextClassLoader();
   }
  }
  return classLoader;
 }
 
 private DefaultFilterSetEntry getFilterSetEntry(String jndiName, Object targetObject)  {
  DefaultFilterSetEntry entry = null;
  // try jndi named first
  System.out.println("** JNDI Name is: " + jndiName);
  DefaultFilterSet filterSet = getFilterSet();
  
  if (filterSet != null) {
   DefaultFilterSetEntry[] entries = filterSet.getFilterSetEntries();
   if (entries != null) {
    for (int i=0; i < entries.length; i++) {
     DefaultFilterSetEntry currentEntry = entries[i];
     String currentEntryJNDIName = currentEntry.getKey().getJndiName();
     String targetInterfaceName = currentEntry.getKey().getTargetClassName();
     String[] targetInterfaceNames = new String[] { targetInterfaceName };
     boolean isJNDINameMatched = WildcardPatternMatcher.matches(currentEntryJNDIName, 
       jndiName, true);
     System.out.println("Is JNDI Name Matched is: " + isJNDINameMatched);
     if (isJNDINameMatched) {
      boolean doInterfacesMatch = doInterfacesMatch(targetInterfaceNames, 
        targetObject);
      if (doInterfacesMatch) {
       entry = currentEntry;
       break;
      }
     }
     
    }
   }
  }
  return entry;
 }
 
 private boolean doInterfacesMatch(String[] targetInterfaceNames, Object targetObject) {
  if ( (targetInterfaceNames != null) && (targetObject != null) ) {
   
   Class[] interfaces = targetObject.getClass().getInterfaces();
   
   if (interfaces != null) {
   
    // first check if the names match
    for (int i=0; i < interfaces.length; i++) {
     String interfaceName = interfaces[i].getName();
     for (int j=0; j < targetInterfaceNames.length; j++) {
      if (WildcardPatternMatcher.matches(targetInterfaceNames[j], interfaceName, true)) {
       System.out.println("*********** Matched Name..");
       return true;
      }
     }
    }
    
    // next check if the target interfacenames are assignable from the interface
    for (int i=0; i < interfaces.length; i++) {
     for (int j=0; j < targetInterfaceNames.length; j++) {
      try
      {
       Class clazz = getClassLoader(interfaces[i]).loadClass(targetInterfaceNames[j]);
       if (clazz.isAssignableFrom(interfaces[i])) {
        System.out.println("*************** Matched Assignable..");
        return true;
       }
      } catch (ClassNotFoundException ignored) {}
     }
    }
   }
   
  }
  return false;
 }
 
    
 private DefaultFilterSet getFilterSet() {
  DefaultFilterSet filterSet = Loader.getFilterSet();
  return filterSet;
 }

 
 private DefaultRequest createRequest(Object object, Method method, Object[] args) {
  return new DefaultRequest(object, method, args);
 }
 
 
 private DefaultResponse createResponse() {
  return new DefaultResponse();
 }
  
  
 private DefaultFilterChain createFilterChain(Object object,
   ClassLoader classLoader, 
   DefaultFilterSetEntry filterSetEntry,
   Context context) {
  
  return new DefaultFilterChain(object, classLoader, filterSetEntry, context);
 }
}

Dig Deeper on Software development best practices and processes