selenium: [πŸ› Bug]: Exception in FirefoxDriver constructor after assembly but not during development

What happened?

When using FirefoxDriver in a Java program created using the maven-assembly-plugin, the FirefoxDriver constructor produces an exception and no driver is created. This, despite the usage of WebDriverManager and a functioning installation of Firefox. The issue does not occur when executing the program within IntelliJ IDEA.

How can we reproduce the issue?

Java snippet:

import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.remote.service.DriverService;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;
import java.util.List;
import java.util.ServiceLoader;
import java.util.stream.Collectors;

public class Test {
    public static void main(final String[] args) {
        WebDriverManager.firefoxdriver().setup();

        final List<String> driverServiceBuilders = ServiceLoader.load(DriverService.Builder.class)
                .stream()
                .map(b -> b.type().getName().replaceAll("[A-Za-z.]+\\.", ""))
                .collect(Collectors.toUnmodifiableList());
        System.out.println(driverServiceBuilders);

        final var options = new FirefoxOptions();
        options.setHeadless(true);
        final var driver = new FirefoxDriver(options);
        final var wait = new WebDriverWait(driver, Duration.ofSeconds(10));

        try {
            // ...
        } catch (final WebDriverException e) {
            // ...
        } finally {
            driver.quit();
        }
    }
}

Corresponding Maven configuration:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>test</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>11</java.version>
        <resource.delimiter>@</resource.delimiter>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.0.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.github.bonigarcia/webdrivermanager -->
        <dependency>
            <groupId>io.github.bonigarcia</groupId>
            <artifactId>webdrivermanager</artifactId>
            <version>5.0.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.32</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.3.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <archive>
                                <manifest>
                                    <mainClass>Test</mainClass>
                                </manifest>
                            </archive>
                            <descriptorRefs>
                                <descriptorRef>jar-with-dependencies</descriptorRef>
                            </descriptorRefs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Relevant log output

Running the code from within IntelliJ IDEA gives the following log:

[main] INFO io.github.bonigarcia.wdm.WebDriverManager - Using geckodriver 0.30.0 (resolved driver for Firefox 93)
[main] INFO io.github.bonigarcia.wdm.WebDriverManager - Exporting webdriver.gecko.driver as [...]\.cache\selenium\geckodriver\win64\0.30.0\geckodriver.exe
[ChromeDriverService$Builder, EdgeDriverService$Builder, GeckoDriverService$Builder, XpiDriverService$Builder, InternetExplorerDriverService$Builder, OperaDriverService$Builder, SafariDriverService$Builder, SafariTechPreviewDriverService$Builder]
1635979091778	geckodriver	INFO	Listening on 127.0.0.1:58965
1635979092654	mozrunner::runner	INFO	Running command: "C:\\Program Files\\Mozilla Firefox\\firefox.exe" "--marionette" "-headless" "--remote-debugging-port" "53683" "-no-remote" "-profile" "[...]"
*** You are running in headless mode.
1635979092860	Marionette	INFO	Marionette enabled
(... more successful geckodriver stuff ...)

Whereas the assembled jar outputs this:

[main] INFO io.github.bonigarcia.wdm.WebDriverManager - Using geckodriver 0.30.0 (resolved driver for Firefox 93)
[main] INFO io.github.bonigarcia.wdm.WebDriverManager - Exporting webdriver.gecko.driver as [...]\.cache\selenium\geckodriver\win64\0.30.0\geckodriver.exe
[ChromeDriverService$Builder]
Exception in thread "main" org.openqa.selenium.WebDriverException: Build info: version: '4.0.0', revision: '3a21814679'
System info: host: '[...]', ip: '[...]', os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '11.0.11'
Driver info: driver.version: FirefoxDriver
        at java.base/java.util.Optional.orElseThrow(Optional.java:408)
        at org.openqa.selenium.firefox.FirefoxDriver.toExecutor(FirefoxDriver.java:230)
        at org.openqa.selenium.firefox.FirefoxDriver.<init>(FirefoxDriver.java:186)
        at Test.main(Test.java:25)

Note especially that the list of available driver service builders is reduced from eight instances to one. Internet Explorer, Edge, Chrome, and Firefox are all installed on my system, and I have not installed any drivers outside of those managed by WebDriverManager.

Operating System

Windows 10

Selenium version

4.0.0

What are the browser(s) and version(s) where you see this issue?

Firefox latest

What are the browser driver(s) and version(s) where you see this issue?

GeckoDriver 0.30.0

Are you using Selenium Grid?

No response

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 2
  • Comments: 16 (6 by maintainers)

Commits related to this issue

Most upvoted comments

The proposed solution is a workaround that only works in Maven - the root problem, however, remains unsolved. Since everything worked fine on 3.141.59 (as I noted above), I believe this regression was introduced with the legacy driver split for Firefox back in November 2018. That said, the issue is exclusive to Firefox:

FirefoxDriver#toExecutor checks, at runtime, whether there is any file named META-INF/services/org.openqa.selenium.remote.service.DriverService$Builder in the classpath whose class is a FirefoxDriverService extension and satisfies the isLegacy flag as required. When, during packaging, the service descriptors for all but the first DriverService go missing, this check will not find any FirefoxDriverService even though the classes for the GeckoDriverService are present.

Bypassing toExecutor, such as by calling new FirefoxDriver(GeckoDriverService.createDefaultService(), options), also bypasses the issue entirely.

Assuming this is possible, I believe the issue should be resolved by correcting the legacy check to not rely on the service entry in the classpath. Independent of that, the exception message needs to be improved. Since the issue apparently occurs in both Maven and Gradle in their default configurations, a lot of unsuspecting developers will encounter it. A googleable message, even if it only refers to a missing (non-)legacy Firefox driver service, will help with finding resources for workarounds. best placed in the Selenium documentation.

Thank you for providing the details and sharing the findings. @jonas-haeusler Your diagnosis for the problem is right. However, after spending sufficient time digging into and seeing how Selenium packages things, I don’t think there is something we are missing there. Such a use case comes up every now and then when there are multiple implementations for the provider. It is essentially how the assembly handles it. I also spent time trying out the metaInf-services handler of the maven-assembly-plugin, initially faced an issue with different plugin versions but got that working. I hope this helps since maven-assembly-plugin provides a mechanism to handle this. The assembly file:

<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
  <!-- TODO: a jarjar format would be better -->
  <id>jar-with-dependencies</id>
  <formats>
    <format>jar</format>
  </formats>
  <includeBaseDirectory>false</includeBaseDirectory>
  <dependencySets>
    <dependencySet>
      <outputDirectory>/</outputDirectory>
      <useProjectArtifact>true</useProjectArtifact>
      <unpack>true</unpack>
      <scope>runtime</scope>
    </dependencySet>
  </dependencySets>
  <containerDescriptorHandlers>
    <containerDescriptorHandler>
      <handlerName>metaInf-services</handlerName>
    </containerDescriptorHandler>
  </containerDescriptorHandlers>
</assembly>

Sharing the pom.xml below:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>groupId</groupId>
  <artifactId>tutorial</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <properties>
    <java.version>9</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-assembly-plugin</artifactId>
      <version>3.3.0</version>
      <type>maven-plugin</type>
    </dependency>
<!--    <dependency>-->
<!--      <groupId>org.seleniumhq.selenium</groupId>-->
<!--      <artifactId>selenium-devtools-v87</artifactId>-->
<!--      <version>4.0.0-beta-3</version>-->
<!--    </dependency>-->
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.8.0</version>
    </dependency>
    <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>4.0.0</version>
    </dependency>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>24.0-jre</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.6.0</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
    <dependency>
      <groupId>org.assertj</groupId>
      <artifactId>assertj-core</artifactId>
      <version>3.17.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>7.4.0</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.opentelemetry</groupId>
      <artifactId>opentelemetry-exporter-jaeger</artifactId>
      <version>1.0.0</version>
      <exclusions>
        <exclusion>  <!-- declare the exclusion here -->
          <groupId>io.opentelemetry</groupId>
          <artifactId>opentelemetry-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.grpc/grpc-netty-shaded -->
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-netty-shaded</artifactId>
      <version>1.42.1</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
<!--      <plugin>-->
<!--        <groupId>org.apache.maven.plugins</groupId>-->
<!--        <artifactId>maven-dependency-plugin</artifactId>-->
<!--        <executions>-->
<!--          <execution>-->
<!--            <id>copy-dependencies</id>-->
<!--            <phase>prepare-package</phase>-->
<!--            <goals>-->
<!--              <goal>copy-dependencies</goal>-->
<!--            </goals>-->
<!--            <configuration>-->
<!--              <outputDirectory>-->
<!--                ${project.build.directory}/libs-->
<!--              </outputDirectory>-->
<!--            </configuration>-->
<!--          </execution>-->
<!--        </executions>-->
<!--      </plugin>-->
<!--      <plugin>-->
<!--        <groupId>org.apache.maven.plugins</groupId>-->
<!--        <artifactId>maven-jar-plugin</artifactId>-->
<!--        <configuration>-->
<!--          <archive>-->
<!--            <manifest>-->
<!--              <addClasspath>true</addClasspath>-->
<!--              <classpathPrefix>libs/</classpathPrefix>-->
<!--              <mainClass>-->
<!--                com.company.MainHttpFirefox-->
<!--              </mainClass>-->
<!--            </manifest>-->
<!--          </archive>-->
<!--        </configuration>-->
<!--      </plugin>-->


      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <inherited>true</inherited>
        <version>3.3.0</version>
        <configuration>
          <appendAssemblyId>true</appendAssemblyId>
          <descriptors>
            <descriptor>src/main/assembly/jar-with-dependencies.xml</descriptor>
          </descriptors>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
            <configuration>
              <archive>
                <manifest>
                  <mainClass>
                    com.company.MainHttpFirefox
                  </mainClass>
                  <addClasspath>true</addClasspath>-->
                </manifest>
              </archive>
              <descriptors>
                <descriptor>src/main/assembly/jar-with-dependencies.xml</descriptor>
              </descriptors>
            </configuration>
          </execution>
        </executions>
      </plugin>
<!--      <plugin>-->
<!--        <artifactId>maven-shade-plugin</artifactId>-->
<!--        <version>1.4</version>-->
<!--        <executions>-->
<!--          <execution>-->
<!--            <phase>package</phase>-->
<!--            <goals>-->
<!--              <goal>shade</goal>-->
<!--            </goals>-->
<!--            <configuration>-->
<!--              <finalName>${project.artifactId}-by-shade</finalName>-->
<!--              <transformers>-->
<!--                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>-->
<!--                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">-->
<!--                  <mainClass>com.company.MainHttpFirefox</mainClass>-->
<!--                </transformer>-->
<!--              </transformers>-->
<!--            </configuration>-->
<!--          </execution>-->
<!--        </executions>-->
<!--      </plugin>-->
    </plugins>
  </build>

</project>

I simply do: mvn clean package

Running the appropriate jar: java -cp /Users/Puja/Documents/GitHub/selenium-java-webdriver-test/target/tutorial-1.0-SNAPSHOT-jar-with-dependencies.jar com.company.MainHttpFirefox

In addition, using the maven-shade plugin, also works like a charm. The commented-out plugins in the pom shared above include the other maven plugins that work as well.

Sharing some references and the similar situations faced that I found on the Internet that helped me understand :

  1. https://dzone.com/articles/jar-deps-dont-meta
  2. http://www.ostack.cn/?qa=942526/
  3. https://stackoverflow.com/questions/47310215/merging-meta-inf-services-files-with-maven-assembly-plugin
  4. https://issues.apache.org/jira/browse/MASSEMBLY-209 (Similar issue was faced by Spring Framework META-INF - https://issues.apache.org/jira/browse/MASSEMBLY-360)

I recommend trying out the custom assembly file approach since that is the solution provided by the assembly plugins themselves. @tobli @Tejareddy1