How can a service factory instantiated bean have access to a httpServletRequest object?

1.3k views Asked by At

Ok, I had a bounty about this same subject with only a partial answer so I'm going to open this one and simplify the subject since I guess the original one was way too bloated.

I'm facing a situation where I basically have a filter that gets a Spring service factory object from the servlet context, This factory allocates an instance of a bean when the filter calls it's getService method, but I need access the session or the actual httpServletRequest object of the request since I need the userID from the request. I think, from what I have read, that I shouldn't pass it down to the bean not just because it is not thread safe but because it will break the abstraction of the remote services bridge (CP2JavaWS) that owns the filter.

How can I get this bean to access the session or the httpServletRequest??

I try to use FacesContext but it didn't worked since I think the bean wasn't instantiated by a jsp call but from a filter.

Now Some code.

this is my web.xml

<display-name>CP2JavaWSTest</display-name>
<welcome-file-list>
    <welcome-file>index.html</welcome-file>
</welcome-file-list>

<error-page>
  <error-code>404</error-code>
  <location>/err.jsp</location>
</error-page>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>
    org.springframework.web.context.request.RequestContextListener
</listener-class>

<listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
    <listener-class>com.cp2javaws.listeners.SpringContextWrapperListener</listener-class>
</listener>

<listener>
    <listener-class>com.bucle.listeners.BCLUserDatabaseContextListener</listener-class>
</listener>

<filter>
    <filter-name>BCLAuthenticationFilter</filter-name>
    <filter-class>com.bucle.filters.BCLAuthenticationFilter</filter-class>
</filter>

<filter>
    <filter-name>BCLAuthorizationFilter</filter-name>
    <filter-class>com.bucle.filters.BCLAuthorizationFilter</filter-class>
</filter>
<filter>
    <filter-name>CPJSonFilter</filter-name>
    <filter-class>com.cp2javaws.filters.CPJSonFilter</filter-class>

</filter>

the application context:

  <bean id="service1" class="com.bucle.database.BCLDb4oManager" scope="request"/>
  <bean id="service2" class="com.bucle.services.appe.BCLUserCFVC"/>
  <bean id="service3" class="com.bucle.services.appe.BCLUserCustomizedFutureValueCalculator"/>

the CPWJavaWS filter

public class CPJSonFilter implements Filter {

    //.. properties

    public void init(FilterConfig filterConfig) 
    throws ServletException {
    //.. init code
    }

    public void destroy() {
        //..
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
        throws IOException, ServletException {


        HttpSession session = ((HttpServletRequest)request).getSession(true); 

        //..Class and method decoding and mapping from JSON

                Class type = null;
                String value = request.getParameter(CP2JavaWSRequestParameterType+paramOrderString);
                if(paramName.indexOf(CP2JavaWSRequestGenericParamSuffix)>0 || request.getParameter(CP2JavaWSRequestParameterType+paramOrderString).equals("id")) {//SAMIR
                    type = Object.class;
                } else {
                    if(paramName.indexOf(CP2JavaWSRequestNullParamSuffix)>0) {
                        String CPClassName = paramName.substring(paramName.indexOf(CP2JavaWSRequestNullParamSuffix)+CP2JavaWSRequestNullParamSuffix.length());
                        try {
                            type = getJavaClassForCPClass(CPClassName);
                        } catch (CP2JavaWSException e) {
                            throw new ServletException("Cannot find corresponding Java class for null argument at index "+paramOrderString+" (passed CP class name is"+CPClassName+")");
                        }
                    } else if(List.class.isAssignableFrom(convertedObject.getClass())) {
                        type = List.class;
                    } else if(Map.class.isAssignableFrom(convertedObject.getClass())) { 
                        type = Map.class;
                    } else {
                        type = convertedObject.getClass();
                    }
                }
                typesOrderedMap.put(new Integer(paramOrderString), type);
            }
        }
        // invoke the service method using the provided service factory class
        Object result = null;
        try {
                Class serviceInterfaceClass = Class.forName(serviceInterfaceName);
                ServiceFactory serviceFactory = (ServiceFactory) filterConfig.getServletContext().getAttribute(ServiceFactory.CP2JAVAWS_SERVICES_FACTORY);
                Object service = serviceFactory.getService(serviceInterfaceClass);


                Method method = service.getClass().getDeclaredMethod(serviceMethodName, (Class[]) typesOrderedMap.values().toArray(new Class[typesOrderedMap.size()]));
                result = method.invoke(service, argumentsOrderedMap.values().toArray());

        } catch(Exception e) {
            throw new ServletException("Error invoking the service method :"+serviceMethodName+" on service "+serviceInterfaceName,e);
        }

        //...Convert result to JSON
            response.setContentType("application/json");
            PrintWriter writer = response.getWriter();
    StringBuffer stb = new StringBuffer();

            //... append result and some other stuff to the string buffer stb

        response.setContentLength(stb.length());
        ((HttpServletResponse)response).setStatus(HttpServletResponse.SC_OK);
        writer.write(stb.toString());
        writer.flush();
    }

    public static Class getJavaClassForCPClass(String CPClassName) throws CP2JavaWSException {
    //... returns the matching Class.class according to a default map like: CPArray -> List.class
    }
}

this is the Spring context listener:

public class SpringContextWrapperListener implements ServletContextListener {


    private ServletContext context = null;


    public void contextDestroyed(ServletContextEvent event) {
        this.context = null;
        //log.info("Spring Context Destruido");
    }


    public void contextInitialized(ServletContextEvent event) {
        this.context = event.getServletContext();
        ApplicationContext springContext = (ApplicationContext) WebApplicationContextUtils.getRequiredWebApplicationContext(this.context);
        SpringContextWrapper aSpringContextWrapper = new SpringContextWrapper(springContext);
        this.context.setAttribute(ServiceFactory.CP2JAVAWS_SERVICES_FACTORY, aSpringContextWrapper);
    }
}

and the factory:

public class SpringContextWrapper implements ServiceFactory {

private ApplicationContext springContext;

public SpringContextWrapper(ApplicationContext springContext) {

    this.springContext = springContext;
}

public Object getService(Class serviceInterfaceClass) throws CP2JavaWSException {

    Map services =  this.springContext.getBeansOfType(serviceInterfaceClass);
    Iterator it = services.values().iterator();
    if(it.hasNext()) {
        return it.next();
    }
    throw new CP2JavaWSException("can't find service for interface "+serviceInterfaceClass.getName());
}

public Object getService(String serviceName) throws CP2JavaWSException {

    Object service = this.springContext.getBean(serviceName);
    if(service==null) {
        throw new CP2JavaWSException("can't find service for name "+serviceName);
    }
    return service;
}}
2

There are 2 answers

4
NilsH On BEST ANSWER

I'm not 100% sure I understand the question, but I think you can use the RequestContextHolder for what you're trying to do.

ServletRequestAttributes attrs = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
HttpServletRequest req = attrs.getRequest();

However, seeing some code would help. I suspect that you might achieve what you want with the correct scope of your bean.

3
Jugal Shah On

You can use threadlocal to store a object which wraps original HttpServletRequest and HttpServletResponse in doFilter method of your filter and then access this wrapper object from threadlocal within your bean. Following is a rough implementation of how you would achieve it:

public class WebRequest {

    private HttpServletRequest request;

    private HttpServletResponse response;

    public WebRequest(HttpServletRequest request, HttpServletResponse response) {
        this.request = request;
        this.response = response;
    }   
}

class WebRequestUtils {

    private static ThreadLocal<WebRequest> webRequest;

    public static void setWebRequest(WebRequest value)   {
        webRequest.set(value);
    }

    public static WebRequest getWebRequest() {
        return webRequest.get();
    }
}

class CustomFilter {

    doFilter(HttpServletRequest request, HttpServletResponse response) {
        WebRequest webRequest = new WebRequest(request, response);
        WebRequestUtils.setWebRequest(webRequest);
    }
}

class Bean {

    void someMethod() {
        WebRequest webRequest = WebRequestUtils.getWebRequest();
    }
}