Вопрос

I am using Spring's WebServiceGatewaySupport to connect to a vendor's SOAP Web Service. One of the requirements of this service is that the client must maintain the session cookie sent by the server.

I was able to determine that WebServiceGatewaySupport internally uses the HttpURLConnection class to make requests. Simply calling

CookieHandler.setDefault(new CookieManager());

before the party gets started adds a default cookie manager and everything works delightfully peachy keen on my local Tomcat instance (I even noted a small rainbow appear next to my machine).

But, when I deploy to WebLogic 10.3.6.0, everything goes all Miley Cyrus. It doesn't twerk like it used to and my cookies are being tossed.

I was able to prove WebLogic is the culprit by overriding the get and put methods of the CookieManager. Plenty of action on these in Tomcat. Not a murmur from WebLogic.

    CookieHandler.setDefault(new CookieManager() {
        @Override
        public Map<String, List<String>> get(URI uri, Map<String, List<String>> stringListMap) throws IOException {
            Map<String, List<String>> map =  super.get(uri, stringListMap);
            LOGGER.info("Cop that: " + uri + " " + map);
            return map;
        }

        @Override
        public void put(URI uri, Map<String, List<String>> stringListMap) throws IOException {
            LOGGER.info("Hello sailor: " + uri + " " + stringListMap);
            super.put(uri, stringListMap);
        }
    });
    ((CookieManager)CookieHandler.getDefault()).setCookiePolicy(CookiePolicy.ACCEPT_ALL);

I can only assume there is some kind of "advanced security shenanigans" intended for incoming servlet requests but is being applied to outgoing connections also. I cant find any weblogic deployment descriptor options that would be of any use.

Bugger.

I can probably get it working with Axis but id rather stab myself in the face with a pen.

I'm going home.


Update: Ok, I haven't solved the root cause, but this how I got it working. I was thinking if I could get access to the actual HttpURLConnection object I can do manual cookie management on it. I was able to look at the Spring WS source and set a new MessageSender which works mostly the same.

public class MyClient extends WebServiceGatewaySupport {
    public MyClient(WebServiceMessageFactory messageFactory) {
        super(messageFactory);

        super.getWebServiceTemplate().setMessageSender(new WebServiceMessageSender() {
            @Override
            public WebServiceConnection createConnection(URI uri) throws IOException {
                URL url = uri.toURL();
                URLConnection connection = url.openConnection();
                if (!(connection instanceof HttpURLConnection)) {
                    throw new HttpTransportException("URI [" + uri + "] is not an HTTP URL");
                }
                HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
                prepareConnection(httpURLConnection);

                HttpURLConnectionProxy httpURLConnectionProxy = new HttpURLConnectionProxy(url);
                httpURLConnectionProxy.setHttpURLConnection(httpURLConnection);
                httpURLConnectionProxy.setCookieManager(cookieManager);
                return new MyHttpUrlConnection(httpURLConnectionProxy);
            }

            protected void prepareConnection(HttpURLConnection connection) throws IOException {
                connection.setRequestMethod(HttpTransportConstants.METHOD_POST);
                connection.setUseCaches(false);
                connection.setDoInput(true);
                connection.setDoOutput(true);
                // ORRRRR YEAAHHHHHHH!
                cookieManager.setCookies(connection);
            }

            @Override
            public boolean supports(URI uri) {
                return true;
            }
        });
    }

Another complication was that I needed to set and get cookie data before and after connect() was called. So I made a HttpURLConnectionProxy class which proxies all the method calls to the one generated by url.openConnection() but does cookie stuff after connect();

public void connect() throws IOException {
    httpURLConnection.connect();
    // WOOPWOOPWOOPWOOP!
    cookieManager.storeCookies(httpURLConnection);
}

But it TWERKS

Это было полезно?

Решение

I think you are twisting the CookieManager API intended use. Please reference the documentation and the CookieManager documentation. Your vendor's requirement is to maintain the session cookie sent by the server. For this requirement to be achieved you want TWO steps:

  1. Within the Spring container, wire a Spring bean which holds the method invocation CookieHandler.setDefault(new CookieManager());
  2. Within the client code initialize a URI instance which will identify the cookies in the CookieStore

Step 1

Assuming you are using Spring 3.1 or later, please find below your Configuration Class:

@Configuration
@EnableWebMvc   // this annotation imports the class  WebMvcConfigurationSupport which bootstraps web mvc
@ComponentScan(basePackages = { "com.orgname"  })
public class WebConfig extends WebMvcConfigurerAdapter {

@Bean
public ViewResolver viewResolver() {

    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
    viewResolver.setViewClass(JstlView.class);
    viewResolver.setPrefix("/view/jsp/");
    viewResolver.setSuffix(".jsp");
    return viewResolver;
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}

/**
 * This method invocation bean stands for the method call:
 * CookieHandler.setDefault(new CookieManager());
 * which should be done at the beginning of an HTTP session to bootstrap
 * the Java 6 Http state management mechanism for the application as a whole. 
 * (http://docs.oracle.com/javase/tutorial/networking/cookies/cookiehandler.html)
 * 
 */
@Bean(name="cookieHandlerSetDefaultBean") 
public MethodInvokingFactoryBean methodInvokingFactoryBean() { 
    MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
    methodInvokingFactoryBean.setTargetClass(CookieHandler.class);
    methodInvokingFactoryBean.setTargetMethod("setDefault");
    CookieManager cookieManager = new CookieManager();
    methodInvokingFactoryBean.setArguments(new Object[]{cookieManager}); 
    return methodInvokingFactoryBean; 
}
}

Step 2

Given that your client class is a Spring Service or Component. Please find below its code.

/**
 * This service aggregates the default CookieManager as explained in the API 
 * (http://docs.oracle.com/javase/6/docs/api/java/net/CookieManager.html). 
 * A system-wide CookieManager that is used by the HTTP protocol handler 
 * can be retrieved by calling CookieHandler.getDefault(). 
 * A CookieManager is initialized with aآ CookieStoreآ which manages storage
 * A CookieStore supports add(cookie) and getCookie() methods
 * A CookieStore is responsible of removing Cookie instances which have expired.
 *
 */
@Service(value="serviceConfigBean")
@DependsOn(value="cookieHandlerSetDefault")    //This is the bean initialized in the Configuration class. It is needed to be initialized before the container initializes the Service 
public class ClientCookiesStore {

    private static final Logger logger = LoggerFactory.getLogger(ClientCookiesStore.class);

    protected CookieStore inmemoryCookieStore;

    protected URI clientURI;

/**
 * The @PostConstruct (lifecycle callback method) indicates this method should be invoked after all 
 * dependency injection is complete. Thus helps in initializing any resources needed by the 
 * service.
 * 
 * In this particular initializing method:
 * (as per http://docs.oracle.com/javase/6/docs/api/java/net/CookieManager.html
 *  and http://docs.oracle.com/javase/tutorial/networking/cookies/cookiemanager.html)
 * The CookieHandler default is installed in the application via 
 * a method invoking factory bean, namely "cookieHandlerSetDefault" which 
 * exists in the java configuration file WebConfig.java

 * (1) A cookieManager property needs 2 steps setup as indicated in the code
 * (2) The internal in-memory implementation of the CookieStore interface is initialized 
 *      through the cookieManager defaults. It is assigned to the inmemoryCookieStore property.  
 * (3) Since a CookieStore aggregates many groups of cookies, each group is identified 
 *     by a URI instance. ClientCookiesStore is associated with the Client URI as indicated in 
 *     the code.  
 * 
 * @throws Exception
 */
@PostConstruct
protected void initializeBean() throws Exception {
    //      (1) Step#1 Initialize a CookieManager with the current Http session default 
    //                  which was already set in the configuration class
    CookieManager cookieManager = (CookieManager)CookieHandler.getDefault();    
    //          Step#2 Then set up the CookiePolicy.
    cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
    //      (2) Assign a CookieStore instance to the in-memory cookie store implemented by the API
    inmemoryCookieStore =  cookieManager.getCookieStore();
    //      (3) Initialize URI instance which will identify the Client cookies in the CookieStore 

    try {
        clientURI = new URI("http://vendor.webservices.com/endpoint");
    } catch (URISyntaxException e) {
        throw new Exception("URISyntaxException created while creating a URI instance for url= "+clientUrl);
    }
}

}

All what is left are the 2 methods to add a new cookie and to retrieve the cookie from the inmemory store. Both methods belong to the above ClientCookiesStore Class.

public List<HttpCookie> getCookiesList() throws Exception {
    List<HttpCookie> httpCookiesList = inmemoryCookieStore.get(clientURI);
    return httpCookiesList;
}

public void addCookie(HttpCookie newCookie) {
        inmemoryCookieStore.add(clientURI, newCookie);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top