View Javadoc
1   package us.codecraft.webmagic.downloader.selenium;
2   
3   import java.io.FileReader;
4   import java.io.IOException;
5   import java.net.MalformedURLException;
6   import java.net.URL;
7   import java.util.ArrayList;
8   import java.util.Collections;
9   import java.util.List;
10  import java.util.Properties;
11  import java.util.concurrent.BlockingDeque;
12  import java.util.concurrent.LinkedBlockingDeque;
13  import java.util.concurrent.atomic.AtomicInteger;
14  
15  import org.openqa.selenium.WebDriver;
16  import org.openqa.selenium.chrome.ChromeDriver;
17  import org.openqa.selenium.chrome.ChromeOptions;
18  import org.openqa.selenium.firefox.FirefoxDriver;
19  import org.openqa.selenium.firefox.FirefoxOptions;
20  import org.openqa.selenium.phantomjs.PhantomJSDriver;
21  import org.openqa.selenium.phantomjs.PhantomJSDriverService;
22  import org.openqa.selenium.remote.DesiredCapabilities;
23  import org.openqa.selenium.remote.RemoteWebDriver;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  /**
28   * @author code4crafter@gmail.com <br>
29   *         Date: 13-7-26 <br>
30   *         Time: 下午1:41 <br>
31   */
32  class WebDriverPool {
33  	private Logger logger = LoggerFactory.getLogger(getClass());
34  
35  	private final static int DEFAULT_CAPACITY = 5;
36  
37  	private final int capacity;
38  
39  	private final static int STAT_RUNNING = 1;
40  
41  	private final static int STAT_CLODED = 2;
42  
43  	private AtomicInteger stat = new AtomicInteger(STAT_RUNNING);
44  
45  	/*
46  	 * new fields for configuring phantomJS
47  	 */
48  	private WebDriver mDriver = null;
49  	private boolean mAutoQuitDriver = true;
50  
51  	private static final String DEFAULT_CONFIG_FILE = "/data/webmagic/webmagic-selenium/config.ini";
52  	private static final String DRIVER_FIREFOX = "firefox";
53  	private static final String DRIVER_CHROME = "chrome";
54  	private static final String DRIVER_PHANTOMJS = "phantomjs";
55  
56  	protected static Properties sConfig;
57  	protected static DesiredCapabilities sCaps;
58  
59  	/**
60  	 * Configure the GhostDriver, and initialize a WebDriver instance. This part
61  	 * of code comes from GhostDriver.
62  	 * https://github.com/detro/ghostdriver/tree/master/test/java/src/test/java/ghostdriver
63  	 *
64  	 * @author bob.li.0718@gmail.com
65  	 * @throws IOException
66  	 */
67  	public void configure() throws IOException {
68  		// Read config file
69  		sConfig = new Properties();
70  		String configFile = DEFAULT_CONFIG_FILE;
71  		if (System.getProperty("selenuim_config")!=null){
72  			configFile = System.getProperty("selenuim_config");
73  		}
74  		sConfig.load(new FileReader(configFile));
75  
76  		// Prepare capabilities
77  		sCaps = new DesiredCapabilities();
78  		sCaps.setCapability("takesScreenshot", false);
79  
80  		String driver = sConfig.getProperty("driver", DRIVER_PHANTOMJS);
81  
82  		// Fetch PhantomJS-specific configuration parameters
83  		if (driver.equals(DRIVER_PHANTOMJS)) {
84  			// "phantomjs_exec_path"
85  			if (sConfig.getProperty("phantomjs_exec_path") != null) {
86  				sCaps.setCapability(
87  						PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
88  						sConfig.getProperty("phantomjs_exec_path"));
89  			} else {
90  				throw new IOException(
91  						String.format(
92  								"Property '%s' not set!",
93  								PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY));
94  			}
95  			// "phantomjs_driver_path"
96  			if (sConfig.getProperty("phantomjs_driver_path") != null) {
97  				System.out.println("Test will use an external GhostDriver");
98  				sCaps.setCapability(
99  						PhantomJSDriverService.PHANTOMJS_GHOSTDRIVER_PATH_PROPERTY,
100 						sConfig.getProperty("phantomjs_driver_path"));
101 			} else {
102 				System.out
103 						.println("Test will use PhantomJS internal GhostDriver");
104 			}
105 		}
106 
107 		// Disable "web-security", enable all possible "ssl-protocols" and
108 		// "ignore-ssl-errors" for PhantomJSDriver
109 		// sCaps.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, new
110 		// String[] {
111 		// "--web-security=false",
112 		// "--ssl-protocol=any",
113 		// "--ignore-ssl-errors=true"
114 		// });
115 
116 		ArrayList<String> cliArgsCap = new ArrayList<String>();
117 		cliArgsCap.add("--web-security=false");
118 		cliArgsCap.add("--ssl-protocol=any");
119 		cliArgsCap.add("--ignore-ssl-errors=true");
120 		sCaps.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS,
121 				cliArgsCap);
122 
123 		// Control LogLevel for GhostDriver, via CLI arguments
124 		sCaps.setCapability(
125 				PhantomJSDriverService.PHANTOMJS_GHOSTDRIVER_CLI_ARGS,
126 				new String[] { "--logLevel="
127 						+ (sConfig.getProperty("phantomjs_driver_loglevel") != null ? sConfig
128 								.getProperty("phantomjs_driver_loglevel")
129 								: "INFO") });
130 
131 		// String driver = sConfig.getProperty("driver", DRIVER_PHANTOMJS);
132 
133 		// Start appropriate Driver
134 		if (isUrl(driver)) {
135 			sCaps.setBrowserName("phantomjs");
136 			mDriver = new RemoteWebDriver(new URL(driver), sCaps);
137 		} else if (driver.equals(DRIVER_FIREFOX)) {
138 			mDriver = new FirefoxDriver(new FirefoxOptions(sCaps));
139 		} else if (driver.equals(DRIVER_CHROME)) {
140 			mDriver = new ChromeDriver(new ChromeOptions().merge(sCaps));
141 		} else if (driver.equals(DRIVER_PHANTOMJS)) {
142 			mDriver = new PhantomJSDriver(sCaps);
143 		}
144 	}
145 
146 	/**
147 	 * check whether input is a valid URL
148 	 *
149 	 * @author bob.li.0718@gmail.com
150 	 * @param urlString urlString
151 	 * @return true means yes, otherwise no.
152 	 */
153 	private boolean isUrl(String urlString) {
154 		try {
155 			new URL(urlString);
156 			return true;
157 		} catch (MalformedURLException mue) {
158 			return false;
159 		}
160 	}
161 
162 	/**
163 	 * store webDrivers created
164 	 */
165 	private List<WebDriver> webDriverList = Collections
166 			.synchronizedList(new ArrayList<WebDriver>());
167 
168 	/**
169 	 * store webDrivers available
170 	 */
171 	private BlockingDeque<WebDriver> innerQueue = new LinkedBlockingDeque<WebDriver>();
172 
173 	public WebDriverPool(int capacity) {
174 		this.capacity = capacity;
175 	}
176 
177 	public WebDriverPool() {
178 		this(DEFAULT_CAPACITY);
179 	}
180 
181 	/**
182 	 *
183 	 * @return
184 	 * @throws InterruptedException
185 	 */
186 	public WebDriver get() throws InterruptedException {
187 		checkRunning();
188 		WebDriver poll = innerQueue.poll();
189 		if (poll != null) {
190 			return poll;
191 		}
192 		if (webDriverList.size() < capacity) {
193 			synchronized (webDriverList) {
194 				if (webDriverList.size() < capacity) {
195 
196 					// add new WebDriver instance into pool
197 					try {
198 						configure();
199 						innerQueue.add(mDriver);
200 						webDriverList.add(mDriver);
201 					} catch (IOException e) {
202 						e.printStackTrace();
203 					}
204 
205 					// ChromeDriver e = new ChromeDriver();
206 					// WebDriver e = getWebDriver();
207 					// innerQueue.add(e);
208 					// webDriverList.add(e);
209 				}
210 			}
211 
212 		}
213 		return innerQueue.take();
214 	}
215 
216 	public void returnToPool(WebDriver webDriver) {
217 		checkRunning();
218 		innerQueue.add(webDriver);
219 	}
220 
221 	protected void checkRunning() {
222 		if (!stat.compareAndSet(STAT_RUNNING, STAT_RUNNING)) {
223 			throw new IllegalStateException("Already closed!");
224 		}
225 	}
226 
227 	public void closeAll() {
228 		boolean b = stat.compareAndSet(STAT_RUNNING, STAT_CLODED);
229 		if (!b) {
230 			throw new IllegalStateException("Already closed!");
231 		}
232 		for (WebDriver webDriver : webDriverList) {
233 			logger.info("Quit webDriver" + webDriver);
234 			webDriver.quit();
235 			webDriver = null;
236 		}
237 	}
238 
239 }