问题是当使用多个线程时cookie已被覆盖。
我有一个WebBot接口,其中包含发送请求的getHtmlResult方法并获取html页面结果:
import java.util.Date;
public interface WebBot {
public String getHtmlResult(Date pickedDate) throws Exception;
}
两个工具是WebBotA和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;
}
}
我使用实现Runnable的WebBotThread.java来运行多个线程:
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类:
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();
}
}
}
我的applicationContext-thread.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">
<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>
我的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>
我的问题是,当我在applicationContext-thread.xml中只设置一个线程池时,两个WebBotA和WebBotB运行OK。但是,当我设置2个线程池同时运行WebBotA和WebBotB时,两个WebBot实现中的一个将获得异常。 我发现这topic的Wurstbro可能与我有同样的问题,但是,当我的解决方案应用于我的上下文时,我也失败了。 我已经研究了很多,但仍然找不到解决我问题的方法。 请帮帮我,非常感谢。
P / s:原谅我,我的英语不好。
答案 0 :(得分:1)
您在URL实例上调用openConnection。这会返回一个HttpUrlConnection实例。
默认Java只会通过标准UrlStreamHandlerFactory创建一次UrlStreamHandler。这意味着,如果您的应用程序使用多个UrlConnection,则它始终使用相同的UrlStreamHandler。这就是您分享Cookie的原因。
如何解决?
或者你创建自己的UrlStreamHandlerFactory并强制使用ThreadLocals以获得不同的cookie。对我来说似乎不容易。
或者您切换到另一个库来处理URL。我鼓励使用Apache Http Client库中的HttpClient。如果您实例化2个HttpClient,他们将无法共享cookie。
正如我所看到您使用的是Spring Framework,您在使用Apache Http Client库时肯定会遇到问题。
如果你实施它就会大喊大叫!