View Javadoc

1   package com.atlassian.webdriver;
2   
3   import com.atlassian.browsers.BrowserConfig;
4   import com.atlassian.webdriver.browsers.AutoInstallConfiguration;
5   import com.atlassian.webdriver.utils.WebDriverUtil;
6   import com.google.common.base.Preconditions;
7   import com.google.common.base.Supplier;
8   import org.openqa.selenium.WebDriver;
9   import org.openqa.selenium.WebDriverException;
10  import org.openqa.selenium.ie.InternetExplorerDriver;
11  import org.slf4j.Logger;
12  import org.slf4j.LoggerFactory;
13  
14  import java.lang.ref.WeakReference;
15  import java.util.Map;
16  import java.util.concurrent.ConcurrentHashMap;
17  
18  import static com.atlassian.webdriver.utils.WebDriverUtil.getUnderlyingDriver;
19  
20  /**
21   * <p/>
22   * Simple lifecycle aware Webdriver helper that will setup auto browsers and then retrieve a driver from the factory.
23   * Once the driver is running it will be re-used if the same browser property is retrieved again.
24   *
25   * <p/>
26   * When the runtime is shutdown it will handle cleaning up the browser. It also provides a way to manually quit all
27   * the registered drivers/browsers via {@link #shutdown()}. When that method is called, the shutdown hooks for those
28   * browser will be unregistered.
29   *
30   * @since 2.1
31   */
32  public class LifecycleAwareWebDriverGrid
33  {
34      private static final Logger log = LoggerFactory.getLogger(LifecycleAwareWebDriverGrid.class);
35      private final static Map<String,AtlassianWebDriver> drivers = new ConcurrentHashMap<String, AtlassianWebDriver>();
36      private volatile static AtlassianWebDriver currentDriver;
37  
38      private static final Map<String,WeakReference<Thread>> SHUTDOWN_HOOKS = new ConcurrentHashMap<String, WeakReference<Thread>>();
39  
40      private LifecycleAwareWebDriverGrid() {}
41  
42      /**
43       * Retrieves the driver from the {@link WebDriverFactory}. If an instance
44       * of the driver is already running then it will be re-used instead of
45       * creating a new instance.
46       *
47       * @return an instance of the driver
48       */
49      public static AtlassianWebDriver getDriver()
50      {
51          String browserProperty = WebDriverFactory.getBrowserProperty();
52          if (browserIsConfigured(browserProperty))
53          {
54              AtlassianWebDriver driver = drivers.get(browserProperty);
55              currentDriver = driver;
56              return driver;
57          }
58  
59          BrowserConfig browserConfig = AutoInstallConfiguration.setupBrowser();
60          AtlassianWebDriver driver = WebDriverFactory.getDriver(browserConfig);
61          drivers.put(browserProperty, driver);
62          currentDriver = driver;
63  
64          addShutdownHook(browserProperty, driver);
65          return driver;
66      }
67  
68      public static AtlassianWebDriver getCurrentDriver()
69      {
70          Preconditions.checkState(currentDriver != null, "The current driver has not been initialised");
71          return currentDriver;
72      }
73  
74      public static Supplier<AtlassianWebDriver> currentDriverSupplier()
75      {
76          return new Supplier<AtlassianWebDriver>()
77          {
78              @Override
79              public AtlassianWebDriver get()
80              {
81                  return getCurrentDriver();
82              }
83          };
84      }
85  
86      /**
87       * A manual shut down of the registered drivers. This basically resets the grid to a blank state. The shutdown hooks
88       * for the drivers that have been closed are also removed.
89       *
90       */
91      public static void shutdown()
92      {
93          for (Map.Entry<String,AtlassianWebDriver> driver: drivers.entrySet())
94          {
95              quit(driver.getValue());
96              removeHook(driver);
97          }
98          drivers.clear();
99          SHUTDOWN_HOOKS.clear();
100         currentDriver = null;
101     }
102 
103     private static void removeHook(Map.Entry<String, AtlassianWebDriver> driver)
104     {
105         WeakReference<Thread> hookRef = SHUTDOWN_HOOKS.get(driver.getKey());
106         final Thread hook = hookRef != null ? hookRef.get() : null;
107         if (hook != null)
108         {
109             Runtime.getRuntime().removeShutdownHook(hook);
110         }
111     }
112 
113     private static void quit(AtlassianWebDriver webDriver)
114     {
115         try
116         {
117             webDriver.quit();
118         }
119         catch (WebDriverException e)
120         {
121             onQuitError(webDriver, e);
122         }
123     }
124 
125 
126     private static boolean browserIsConfigured(String browserProperty)
127     {
128         return drivers.containsKey(browserProperty);
129     }
130 
131     private static void addShutdownHook(final String browserProperty, final WebDriver driver) {
132         final Thread quitter = new Thread()
133         {
134             @Override
135             public void run()
136             {
137                 log.debug("Running shut down hook for {}", driver);
138                 try
139                 {
140                     drivers.remove(browserProperty);
141                     if (driver.equals(currentDriver))
142                     {
143                         currentDriver = null;
144                     }
145                     log.info("Quitting {}", getUnderlyingDriver(driver));
146                     driver.quit();
147                     log.debug("Finished shutdown hook {}", this);
148                 }
149                 catch (WebDriverException e)
150                 {
151                     onQuitError(driver, e);
152                 }
153             }
154         };
155         SHUTDOWN_HOOKS.put(browserProperty, new WeakReference<Thread>(quitter));
156         Runtime.getRuntime().addShutdownHook(quitter);
157     }
158 
159 
160     private static void onQuitError(WebDriver webDriver, WebDriverException e)
161     {
162         // there is no sense propagating the exception, and in 99 cases out of 100, the browser is already dead if an
163         // exception happens
164         log.warn("Exception when trying to quit driver {}: {}", webDriver, e.getMessage());
165         log.debug("Exception when trying to quit driver - details", e);
166     }
167 
168 }