java - cookie overried in multi-thread

拟墨画扇 提交于 2019-12-13 04:07:52

问题


The problem is that the cookie has been override when using multiple threads.

I have a WebBot interface that contains getHtmlResult method to sent request and get html page result:

import java.util.Date;

public interface WebBot {
    public String getHtmlResult(Date pickedDate) throws Exception;
}

Two implement are WebBotA and WebBotB: WebBotA:

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

@Service("webBotA")
@Scope("prototype")
public class WebBotA implements WebBot{
    String urlHeader = "http://booknow.jetstar.com";
    String urlTailBeforeRedirect = "/Search.aspx?culture=vi-VN";
    String charset = "UTF-8";
    String requestMethod = "POST";
    int readTimeOut = 10000;

    @Override
    public String getHtmlResult(Date pickedDate) throws Exception {
        System.out.println("WebBot A started");

        String htmlResult = "";

        DateFormat df = new SimpleDateFormat("dd");
        String pickedDateDay = df.format(pickedDate);

        df = new SimpleDateFormat("yyyy-MM");
        String pickedDateMonth = df.format(pickedDate);


        Map<String, String> requestParams = initRequestParams();
        String postParameters = "ControlGroupSearchView%24AvailabilitySearchInputSearchView%24DropDownListCurrency=VND&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24DropDownListFareTypes=I&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24"
                + "DropDownListMarketDay1=" + pickedDateDay
                + "&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24DropDownListMarketDay2=1&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24DropDownListMarketDay3=&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24"
                + "DropDownListMarketMonth1=" + pickedDateMonth
                + "&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24DropDownListMarketMonth2=1968-1&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24DropDownListMarketMonth3=&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24"
                + "DropDownListPassengerType_ADT=" + 1
                + "&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24"
                + "DropDownListPassengerType_CHD=" + 0
                + "&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24"
                + "DropDownListPassengerType_INFANT=" + 0
                + "&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24RadioButtonMarketStructure=OneWay&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24"
                + "TextBoxMarketDestination1=" + "HAN"
                + "&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24TextBoxMarketDestination2=&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24TextBoxMarketDestination3=&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24"
                + "TextBoxMarketOrigin1=" + "SGN"
                + "&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24TextBoxMarketOrigin2=&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24TextBoxMarketOrigin3=&ControlGroupSearchView%24ButtonSubmit=&__VIEWSTATE=%2FwEPDwUBMGQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgEFJ01lbWJlckxvZ2luU2VhcmNoVmlldyRtZW1iZXJfUmVtZW1iZXJtZSDCMtVG%2F1lYc7dy4fVekQjBMvD5&culture=vi-VN&date_picker=&go-booking=&pageToken=sLkmnwXwAsY%3D&ControlGroupSearchView%24AvailabilitySearchInputSearchView%24fromCS=yes";

            try {
                // Cookie manager registry
                CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));

                URL obj = new URL(urlHeader + urlTailBeforeRedirect);
                HttpURLConnection conn = (HttpURLConnection) obj.openConnection();

                // Set redirect to false
                conn.setInstanceFollowRedirects(false);
                conn.setReadTimeout(readTimeOut);
                conn.setRequestMethod(requestMethod);
                for (Map.Entry<String, String> requestParamEntry : requestParams.entrySet()) {
                    conn.setRequestProperty(requestParamEntry.getKey(), requestParamEntry.getValue());
                }

                // Send post request
                conn.setDoOutput(true);
                DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
                wr.writeBytes(postParameters);
                wr.flush();
                wr.close();

                // get redirect url from "location" header field
                String urlTailAfterRedirect = conn.getHeaderField("Location");

                // get the cookie if need, for login
                String cookies = conn.getHeaderField("Set-Cookie");

                // open the new connnection again
                conn = (HttpURLConnection) new URL(urlHeader + urlTailAfterRedirect).openConnection();
                conn.setRequestProperty("Cookie", cookies);

                conn.setReadTimeout(readTimeOut);
                conn.setRequestMethod(requestMethod);
                for (Map.Entry<String, String> requestParamEntry : requestParams.entrySet()) {
                    conn.setRequestProperty(requestParamEntry.getKey(), requestParamEntry.getValue());
                }

                conn.setDoOutput(true);
                wr = new DataOutputStream(conn.getOutputStream());
                wr.writeBytes(postParameters);
                wr.flush();
                wr.close();

                String inputLine;

                InputStream connectionInputStream = conn.getInputStream();
                String contentEncoding = conn.getContentEncoding();
                if(contentEncoding != null){
                    if (contentEncoding.toLowerCase().contains("gzip")) {
                        connectionInputStream = new GZIPInputStream(connectionInputStream);
                    }   
                }
                else{
                    throw new IOException("Failed to get Content Encoding. May by cause Cookie Manager");
                }

                BufferedReader in = new BufferedReader(new InputStreamReader(connectionInputStream, charset));

                StringBuffer html = new StringBuffer();

                while ((inputLine = in.readLine()) != null) {
                    html.append(inputLine);
                }
                in.close();

                htmlResult =html.toString();

            }
            catch (IOException e) {
                System.out.println(e.toString());
            }
            finally{
            }
            return htmlResult;
        }

    private Map<String, String> initRequestParams(){
        Map<String, String> requestParams = new HashMap<String, String>();
        requestParams.put("User-Agent", "Mozilla/5.0");
        requestParams.put("Accept-Language", "en-US,en;q=0.5");
        requestParams.put("Content-Type", "application/x-www-form-urlencoded");
        requestParams.put("Accept-Charset", charset);
        requestParams.put("Host", "booknow.jetstar.com");
        requestParams.put("Referer", "http://www.jetstar.com/vn/vi/home");
        requestParams.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        requestParams.put("Accept-Encoding", "gzip, deflate");
        requestParams.put("Connection", "keep-alive");
        return requestParams;
    }
}

WebBotB:

@Service("webBotB")
@Scope("prototype")
public class WebBotB implements WebBot{

    String urlNoSessionId = "https://ameliaweb5.intelisys.ca/VietJet/ameliapost.aspx?lang=en";
    String urlWithSessionId = "https://ameliaweb5.intelisys.ca/VIETJET/TravelOptions.aspx?lang=en&st=pb&sesid=";
    String charset = "UTF-8";
    String requestPostMethod = "POST";
    String requestGetMethod = "GET";
    int readTimeOut = 10000;

    @Override
    public String getHtmlResult(Date pickedDate) throws Exception {

        System.out.println("WebBot B started");
        String htmlResult = "";

        DateFormat df = new SimpleDateFormat("dd");
        String departDayStr = df.format(pickedDate);

        df = new SimpleDateFormat("yyyy-MM");
        String departMonthStr = df.format(pickedDate).replaceAll("-", "%2F");

        Date currentDate = new Date();
        df = new SimpleDateFormat("dd");
        String currentDayStr = df.format(currentDate);
        df = new SimpleDateFormat("yyyy-MM");
        String currentMonthStr = df.format(currentDate).replaceAll("-", "%2F");

        Map<String, String> requestParams = initRequestParams();
        String postParameterRequest1 = "chkRoundTrip=&"
                + "lstOrigAP=" + "SGN"
                + "&lstDestAP=" + "HAN"
                + "&dlstDepDate_Day=" + departDayStr
                + "&dlstDepDate_Month=" + departMonthStr
                + "&dlstRetDate_Day=" + departDayStr
                + "&dlstRetDate_Month=" + departMonthStr
                + "&lstCurrency=VND&lstResCurrency=VND&lstDepDateRange=0&lstRetDateRange=0&"
                + "txtNumAdults=" + 1
                + "&txtNumChildren=" + 0
                + "&txtNumInfants=" + 0
                + "&lstLvlService=1&blnFares=False&txtPromoCode=";

        String postParameterRequest2 = "__VIEWSTATE=%2FwEPDwULLTE1MzQ1MjI3MzAPZBYCZg9kFg4CCA8QZGQWAGQCCQ8QZGQWAGQCCw8QZGQWAGQCDQ8QZGQWAGQCEQ8QZGQWAGQCEg8QZGQWAGQCEw8QZGQWAGRkDuhQN17CT5ZIydlFFSt%2BWc8NsCA%3D&__VIEWSTATEGENERATOR=BA3C3B49&SesID=&DebugID=62&lstOrigAP=-1&lstDestAP=-1&"
                + "dlstDepDate_Day=" + currentDayStr
                + "&dlstDepDate_Month=" + currentMonthStr
                + "&lstDepDateRange=0&dlstRetDate_Day=" + currentDayStr
                + "&dlstRetDate_Month=" + departMonthStr
                + "&lstRetDateRange=0"
                + "&txtNumAdults=0"
                + "&txtNumChildren=0"
                + "&txtNumInfants=0"
                + "&lstLvlService=1&lstResCurrency=VND&lstCurrency=VND&txtPromoCode=";

        try{
            CookieHandler.setDefault(null);

            // Begin step 1
            URL objectURLForRequest1_2 = new URL(urlNoSessionId);
            HttpURLConnection connection1 = (HttpURLConnection) objectURLForRequest1_2.openConnection();

            // Set redirect to false
            connection1.setInstanceFollowRedirects(false);
            connection1.setReadTimeout(readTimeOut);
            connection1.setRequestMethod(requestPostMethod);
            for (Map.Entry<String, String> requestParamEntry : requestParams.entrySet()) {
                connection1.setRequestProperty(requestParamEntry.getKey(), requestParamEntry.getValue());
            }
            connection1.setFixedLengthStreamingMode(postParameterRequest1.length());

            // Send post request
            connection1.setDoOutput(true);
            DataOutputStream dataOutputStream = new DataOutputStream(connection1.getOutputStream());
            dataOutputStream.writeBytes(postParameterRequest1);
            dataOutputStream.flush();
            dataOutputStream.close();

            String cookieInRequest1String = connection1.getHeaderField("Set-Cookie");

            // Get session id for request 3
            String sessionIdForRequest3 = "";
            List<HttpCookie> cookies = HttpCookie.parse(cookieInRequest1String);
            for (HttpCookie httpCookie : cookies) {
                if (httpCookie.getName().equals("ASP.NET_SessionId"))
                {
                    sessionIdForRequest3 = httpCookie.getValue();
                    break;
                }
            }

            // Begin step 2
            HttpURLConnection connection2 = (HttpURLConnection) objectURLForRequest1_2.openConnection();
            // Set redirect to false
            connection2.setInstanceFollowRedirects(false);
            connection2.setReadTimeout(readTimeOut);
            connection2.setRequestMethod(requestPostMethod);
            for (Map.Entry<String, String> requestParamEntry : requestParams.entrySet()) {
                connection2.setRequestProperty(requestParamEntry.getKey(), requestParamEntry.getValue());
            }
            connection2.setRequestProperty("Cookie", cookieInRequest1String);
            connection2.setFixedLengthStreamingMode(postParameterRequest2.length());

            // Send post request
            connection2.setDoOutput(true);
            dataOutputStream = new DataOutputStream(connection2.getOutputStream());
            dataOutputStream.writeBytes(postParameterRequest2);
            dataOutputStream.flush();
            dataOutputStream.close();

            // Ommit this connection2.getInputStream will lead to Session Expired !
            @SuppressWarnings("unused")
            InputStream connectionInputStream2 = connection2.getInputStream();

            // Begin step 3
            urlWithSessionId = urlWithSessionId + sessionIdForRequest3;
            URL objectURLForRequest3 = new URL(urlWithSessionId);
            HttpURLConnection connection3 = (HttpURLConnection) objectURLForRequest3.openConnection();
            connection3.setRequestMethod(requestGetMethod);
            connection3.setRequestProperty("Cookie", cookieInRequest1String);
            for (Map.Entry<String, String> requestParamEntry : requestParams.entrySet()) {
                connection3.setRequestProperty(requestParamEntry.getKey(), requestParamEntry.getValue());
            }

            String inputLine;

            InputStream connectionInputStream = connection3.getInputStream();
            String contentEncoding = connection3.getContentEncoding();
            if (connection3.getContentEncoding() != null){
                if (contentEncoding.toLowerCase().contains("gzip")) {
                    connectionInputStream = new GZIPInputStream(connectionInputStream);
                }
                if (contentEncoding.toLowerCase().contains("deflate")){
                    connectionInputStream = new DeflaterInputStream(connectionInputStream);
                }
            }

            BufferedReader in = new BufferedReader(new InputStreamReader(connectionInputStream, charset));

            StringBuffer html = new StringBuffer();

            while ((inputLine = in.readLine()) != null) {
                html.append(inputLine);
            }
            in.close();

            htmlResult = html.toString();

        }
        catch (IOException e) {
            System.out.println(e.toString());
        }
        finally{
        }
        return htmlResult;
    }

    private Map<String, String> initRequestParams(){
        Map<String, String> requestParams = new HashMap<String, String>();
        requestParams.put("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2329.0 Safari/537.36");
        requestParams.put("Accept-Language", "en-US,en;q=0.5");
        requestParams.put("Content-Type", "application/x-www-form-urlencoded");
        requestParams.put("Accept-Charset", charset);
        requestParams.put("Host", "ameliaweb5.intelisys.ca");
        requestParams.put("Referer", "https://ameliaweb5.intelisys.ca/VietJet/ameliapost.aspx?lang=vi");
        requestParams.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        requestParams.put("Accept-Encoding", "gzip, deflate");
        requestParams.put("Connection", "keep-alive");
        return requestParams;
    }
}

I uses WebBotThread.java that implements Runnable to run multiple threads:

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import core.WebBot;
import core.WebBotA;
import core.WebBotB;

@Component("webBotThread")
@Scope("prototype")
public class WebBotThread implements Runnable{
    @Autowired 
    AutowireCapableBeanFactory factory;

    private Date pickedDate;
    private int webBotType;

    public WebBotThread(){}

    @Override
    public void run() {
        WebBot webBot = null;
        if (webBotType == 1)
        {
            webBot = (WebBotA)factory.getBean("webBotA");
        }
        else{
            webBot = (WebBotB)factory.getBean("webBotB");
        }

        try {
            webBot.getHtmlResult(pickedDate);
            if(webBotType == 1)
                System.out.println("WebBotA finished successfully");
            else
                System.out.println("WebBotB finished successfully");
        } catch (Exception e) {
            if(webBotType == 1)
                System.out.println("WebBotA failed: " + e.toString());
            else
                System.out.println("WebBotB failed: " + e.toString());
        }
    }

    public Date getPickedDate() {
        return pickedDate;
    }

    public void setPickedDate(Date pickedDate) {
        this.pickedDate = pickedDate;
    }

    public int getWebBotType() {
        return webBotType;
    }

    public void setWebBotType(int webBotType) {
        this.webBotType = webBotType;
    }

}

My Main.java class:

import java.util.Calendar;
import java.util.Date;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

public class Main {

    public static void main(String[] args) {
        try {
            @SuppressWarnings("resource")
            ApplicationContext context = new ClassPathXmlApplicationContext( "classpath:spring/applicationContext.xml");

            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.YEAR, 2015);
            cal.set(Calendar.MONTH, Calendar.APRIL);
            cal.set(Calendar.DAY_OF_MONTH, 30);
            Date pickedDate = cal.getTime();

            ThreadPoolTaskExecutor threadPoolTaskExecutor = (ThreadPoolTaskExecutor) context.getBean("ticketFinderBoExecutor");

            WebBotThread wbThreadA = context.getBean(WebBotThread.class);
            wbThreadA.setWebBotType(1);
            wbThreadA.setPickedDate(pickedDate);

            WebBotThread wbThreadB = context.getBean(WebBotThread.class);
            wbThreadB.setWebBotType(2);
            wbThreadB.setPickedDate(pickedDate);

            threadPoolTaskExecutor.execute(wbThreadA);
            threadPoolTaskExecutor.execute(wbThreadB);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

My applicationContext-thread.xml settings:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"
    default-autowire="byName">

    <bean id="ticketFinderBoExecutor"
        class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <property name="corePoolSize" value="2" />
        <property name="maxPoolSize" value="2" />
        <property name="WaitForTasksToCompleteOnShutdown" value="true" />
    </bean>
</beans>

My applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"
    default-autowire="byName">
    <context:spring-configured/>

    <import resource="applicationContext-thread.xml" />

    <!-- Declare for annotation -->
    <context:annotation-config />

    <!-- Declare for transaction manager -->
    <tx:annotation-driven />

    <!-- Auto scan Beans to Spring container -->
    <context:component-scan
        base-package="core
                    , helper
                    , thread" />
</beans>

And my problem is that when I set only 1 thread pool in applicationContext-thread.xml, two WebBotA and WebBotB run OK. However, when I set 2 thread pool to run WebBotA and WebBotB concurrently, one of the two WebBot implements will be got exception. I found this topic of Wurstbro maybe have the same problem with me, however, I also failed when applied his solution to my context. I've researched a lot but still not find a solution for my problem. Please help me and thanks a lot.

P/s: Forgive me, my English is not good.


回答1:


You call the openConnection on a URL instance. This is returning you a HttpUrlConnection instance.

Default Java will create a UrlStreamHandler through the standard UrlStreamHandlerFactory only once. This means, if your application uses multiple UrlConnection, it is always using the same UrlStreamHandler. That's the reason why your cookies are shared.

How to fix?

Or you create your own UrlStreamHandlerFactory and force something with ThreadLocals in order to have different cookies. Doesn't seem easy to me.

Or you switch to another library to deal with URLs. I encourage to use HttpClient from Apache Http Client library. If you instantiate 2 HttpClient, they won't share the cookies.

As I see you are using the Spring Framework, you definitely won't have problems to use the Apache Http Client library.

Shout if you get stuck implementing it!



来源:https://stackoverflow.com/questions/29456613/java-cookie-overried-in-multi-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!