2

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;
}}
jigzat
  • 3,498
  • 1
  • 21
  • 23
  • I post this as a comment since i havent used it on factorymethods but only on controllers yet: with Spring you can use the argument resolvers to inject needed objects into your methods. There is a localeargumentresolver as well as the requestargumentresolver that you are able to use in your requestmapping methods. Basically these are contracts on how to handle arguments for methods called by the framework. Probably this is just working on factory methods too? – Martin Frey Apr 30 '13 at 05:03
  • Thank you for answer martin, NilsH answer was very easy to test and it did work but I will look into your answer about argument resolvers more deeply since my Spring knowledge is rudimentary. It might also work. Don't worry I will not down vote it. – jigzat May 01 '13 at 21:05

2 Answers2

2

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.

NilsH
  • 13,705
  • 4
  • 41
  • 59
  • Thank you, let me try that and the I will publish some code. I didn't wanted because I was trying to make the post more readable but at the we express in code. – jigzat Apr 30 '13 at 23:48
  • It actually worked, eureka!, now I have a nice db4o file with each user name that register and logs in. I haven't selected as an definitely answer since I need to test multiple users logging in at the same and I really want a second opinion, no offense please is just that I wish I didn't have to depend on Spring. Do you think I should add a prototype tag to the bean xml configuration??? – jigzat May 01 '13 at 21:08
  • If there's no state in your method besides what you need from the request object, then it need not be prototype. But the reason I asked about som usage examples was because I'm suspecting you might be able to use either the Spring `request` scope, or a custom scope. – NilsH May 01 '13 at 21:18
  • definitely it worked out, thank you very much. Although now I have another issue and is that I need to notify the object when the session gets invalidated or closed. – jigzat May 08 '13 at 06:27
1

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();
    }
}
Jugal Shah
  • 3,621
  • 1
  • 24
  • 35
  • As mentioned by NilsH RequestContextHolder would be another and probably a more clean way to achieve the same thing. – Jugal Shah Apr 30 '13 at 05:26
  • Yeah, that's basically what `RequestContextHolder` do already. – NilsH Apr 30 '13 at 05:58
  • Thank you all for your answers. Yes I saw something in the CP2JavaWS that requested something with threadLocal but it looked a little bit weird. It might be one of those things that looks like a hack but at the end is the right way of doing it. I will check into it. – jigzat Apr 30 '13 at 23:47