Jigsaw test dependencies in classpath: why is the superclass in another JAR not accessible? - maven

I am currently testing to migrate an existing Java 8 (Maven) project to Java 9 / Jigsaw, using jdk9-ea+147 and maven-compiler-plugin 3.6.0.
I have converted the src directory to a module and left the test directory non-modular.
During testCompile I get an unexpected error:
error: build() in ExtractedArtifactStoreBuilder is defined in an inaccessible class or interface
Of course I have checked that both the ExtractedArtifactStoreBuilder is public. build() is inherited from its (public) superclass and public as well. The code compiles and runs fine in JDK 8.
ExtractArtifactStoreBuilder is defined in a different 3rd partry JAR than its superclass, but Maven correctly puts both in the classpath. Confusingly, the superclass has the same classname (but resides in a different package)
As far as I can see, I should be able to access public inherited methods in my test code. So is this a bug in the jdk9 early access version?
Edit: hoping for better comprehensibility, here is a little abstraction of the involved JARs and classes, with less confusing naming and unimportant stuff left out (for the actual dependencies, see below):
process.jar
public ProcessStoreBuilder
public ProcessStoreBuilder download(...) // returns "this"
public ... build()
mongo.jar
public MongoStoreBuilder extends ProcessStoreBuilder
src/test/java/ExampleTest
mongoStoreBuilder.download(...).build()
// ^ breaks at compile time, saying that
// ProcessStoreBuilder#build() is not accessible
I have been able to reproduce the behavior in a minimal setup:
pom.xml (excerpt)
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>9</source>
<target>9</target>
<debug>true</debug>
<optimize>true</optimize>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>1.50.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.process</artifactId>
<version>1.50.0</version>
<scope>test</scope>
</dependency>
</dependencies>
src/main/java/module-info.java
module com.mycompany.example.testdependencyexample {}
src/test/java/ExampleTest.java (excerpt)
#Before
public void setUp() throws Exception {
// both `download` and `build` are inherited from the superclass
// the following does work
// de.flapdoodle.embed.mongo.config.ExtractedArtifactStoreBuilder
ExtractedArtifactStoreBuilder easb = new ExtractedArtifactStoreBuilder();
easb.download(new DownloadConfigBuilder().build());
easb.build();
// but this does not
// download returns the same instance but has the superclass as return type
// de.flapdoodle.embed.process.store.ExtractedArtifactStoreBuilder
// the compiler can't see the `build` method of the superclass
new ExtractedArtifactStoreBuilder()
.download(new DownloadConfigBuilder().build())
.build();
}

Related

How to get the configuration in my Custom Annotation Processing whitch was defined in my Maven plugin

My goal is read a parameter's value from costom Maven Plugin
during the processing of process, I need this value to determine whether to process annotations.
Here is what i done:
customize a maven plugin to configure the parameter of funProcess
#Mojo(defaultPhase = LifecyclePhase.COMPILE, threadSafe = true,
requiresDependencyResolution = ResolutionScope.COMPILE)
public class PamirsFunctionMojo extends AbstractMojo {
#Parameter
private Boolean funProcess;
#Override
public void execute() throws MojoExecutionException, MojoFailureException {
//todo
this.getPluginContext().put("funProcess", funProcess);
}
}
this is my maven-plugin's definition.
<groupId>pro.xxx.pamirs.plugin</groupId>
<artifactId>pamirs-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<version>2.0.0-SNAPSHOT</version>
Build a project to process annotation #Fun which is defined in method. So,I need to define A Annotation Processor through extends AbstractProcessor,handle the method corresponding to this #Fun annotation in the process interface.
Using the value in my Annotation Processor.
I need the value of funProcess which is configured within the pom of business project which relying on this Annotation Processor project, then using the value to determine whether to process annotations In the Annotation Processor project.
The test project will rely on annotation processing projects and my maven-plugin.
This is the POM definition .
<dependencies>
<dependency>
<groupId>pro.xxx.pamirs.annotation</groupId>
<artifactId>annotation-processor</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>pro.xxx.pamirs.plugin</groupId>
<artifactId>pamirs-maven-plugin</artifactId>
<version>2.0.0-SNAPSHOT</version>
<executions>
<execution>
<configuration>
<funProcess>false</funProcess>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</dependency>
I want to know how to get the value in my Annotation Processor.

Not able to inject #Service and #Contract dependency in my resource class

On base of the guide from this blog, Roll your own Auto Discovery with Jersey and HK2, I have the follow resource POJO:
#Path("Test")
public class TestResource {
#Inject
private TestService service;
#GET
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Set<Test> getTests() {
return service.getAllTests();
}
}
The TestService:
#Contract
public interface TestService {
public Set<Test> getAllTests();
}
The TestServiceImpl
#Service
public class TestServiceImpl implements TestService {
#Override
public Set<Test> getAllTests() {
Set<Test> tests = new HashSet<>();
Test c = new Test();
c.setName("test");
tests.add(c);
return tests;
}
}
The Jersey dependency in pom.xml is of version 2.25.1
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<!-- use the following artifactId if you don't need servlet 2.x compatibility -->
<!-- artifactId>jersey-container-servlet</artifactId -->
</dependency>
<dependency>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>jaxrs-ri</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2</artifactId>
<version>2.5.0-b36</version>
</dependency>
In order to make Jersey scan the #Service and #Contract classes automatically, I used the inhabitant-generator plugin with version 2.5.0-b36:
<plugin>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-inhabitant-generator</artifactId>
<version>2.5.0-b36</version>
<executions>
<execution>
<goals>
<goal>generate-inhabitants</goal>
</goals>
</execution>
</executions>
</plugin>
There is the corresponding Feature implementation:
public class AutoServiceDiscovery implements Feature {
#Override
public boolean configure(FeatureContext context) {
ServiceLocator locator = ServiceLocatorProvider.getServiceLocator(context);
DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
Populator populator = dcs.getPopulator();
try {
populator.populate(new ClasspathDescriptorFileFinder(this.getClass().getClassLoader()),
new DuplicatePostProcessor());
} catch (IOException | MultiException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
}
return true;
}
}
And it is indeeded registered through my ResourceConfig class:
#ApplicationPath("/*")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
packages("resources");
register(new AutoServiceDiscovery());
}
}
However, I send request to the /test, got the following error:
MultiException has 3 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for
injection at SystemInjecteeImpl(requiredType=TestService,parent=TestResource,qualifiers=
{},position=-1,optional=false,self=false,unqualified=null,1947073589)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of
rx.practice.ee.jaxrs.resources.TestResource errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on
rx.practice.ee.jaxrs.resources.TestResource
org.jvnet.hk2.internal.Collector.throwIfErrors(Collector.java:89)
org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:250)
org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:358)
org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:487)
org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:162)
...
Question: Anyone knows why the #Service class cannot be injected? I am using Tomcat server
After a couple of days research on the source code of inhabitat-generator, I figured out that in case of web application package,war, the locator file is not generated in META-INF/hk2-locator as demonstracted in the HK2 Inhabitant Generator office site in case of using jar as deployment package. The source code of AbstractInhabitantsGeneratorMojo.java told that in case of war, locator files are generated in hk2-locator, and this is not mentioned in the HK2 Inhabitant Generator office site.
However, when constructing the ClasspathDescriptorFileFinder without the directory names argument in the bootstrap class, AutoServiceDiscovery, it is only compatible with jar as deployment package, meaning it is only finding files in META-INF/hk2-locator.
So the better solution would be not to use inhabitant-generator plugin but the metadata-generator dependency, which is an annotation processor at compile time and, it is proved out-of-the-box.
If someone is persistent to using this plugin, he/she could create his/her own ClasspathDescriptorFileFinder so that it is able to find locator files from hk2-locator
Last but not least, I also tried to use the inhabitants-generator plugin's options to generate the locator files in hk2-locator, but this seems to be next to impossible as well

how to create the executable jar file for cucumber project with serenity-gradle -not maven project [duplicate]

I have a project with cucumber and maven also I am using the JUnit.
I am able to run and build my project successfully from Eclipse.
Now I want to run the test from command line in another system which does(should) not have eclipse or cucumber installed. I have an idea that we can create a JAR from jar we can run the tests by java cli commands.
Below are the combinations I am trying to run my tests from , also I am pasting the pom.xml and RunCukesTest.java file..
pom.xml
<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>pmc</groupId>
<artifactId>se</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>storeEnabler</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-testng</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>2.52.0</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.11-beta3</version>
</dependency>
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.8.0</version>
</dependency>
</dependencies>
<build>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/java</directory>
</testResource>
<testResource>
<directory>${project.basedir}/src/test/resource</directory>
</testResource>
</testResources>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<fork>true</fork>
<executable>C:\Program Files\Java\jdk1.8.0_60\bin\javac.exe</executable>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit47</artifactId>
<version>2.18</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>cucumber.api.cli.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
RunCukesTest.java
package se.stepDefinations;
import org.junit.runner.RunWith;
import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
#RunWith(Cucumber.class)
#CucumberOptions(features = "src/test/resource/features/Printing.feature:117", plugin = { "pretty",
"html:target/cucumber-html-report" }, glue = { "se.stepDefinations" }, tags = {})
public class RunCukesTest {
public static void main(String[] args) throws Exception {
}
}
I have added JUNIT Jar in Class path.
I am generating jars in 2 ways,
1) Exporting JAR using - > Project ->Export -> JAR File
Selecting MAIN Class in the last step as : RunCukesTest as I have defined main method here for the entry point(do we require this main method in this class???)
After the export , I am running below command ,
1.1 java -jar xyz.jar
I get error : NoClassDef found : org/junit/runner/JUnitCore
So I ran it this way :
1.2 java -cp xyz.jar;junit-4.12.jar org.junit.runner.JUnitCore
It said,
JUnit version 4.12
Time:0.001
OK(0 tests)
It still didnt work , So I appended the RunCukesTest file namespace at the last,
1.3 java -cp xyz.jar;junit-4.12.jar org.junit.runner.JUnitCore se.stepDefinations.RunCukesTest
It gave me error :Type cucumber.api.junit.Cucumber not present
2) So I gave up on the option for Export of jar and I am trying now to use the JAR from the maven Build.
I selected the POM to run with Maven Build and it created 2 jars in the target folder ,
1 with name xyz-0.0.1-SNAPSHOT with 16kb another with 2nd with
xyz-0.0.1-SNAPSHOT-jar-with-dependencies with 33mb
1) I ran the bigger file with dependencies using
java -jar xyz-0.0.1-SNAPSHOT-jar-with-dependencies.jar
It gave me message :
Got no path to feature directory
2) So I tried appending the namespace to RunCukesTest file,
java -jar xyz-0.0.1-SNAPSHOT-jar-with-dependencies.jar se.stepDefinations.RunCukesTest
I received an error : not a file or directory found
, as of course as the error says , it is trying to find a feature inside the target folder.
Again , I want to run the JAR independent of any such project file dependencies in any other computer like an executable.
Any help would be appreciated. Thanks.
I would divide the problem you are thinking of in two parts.
Create an executable jar
Run Cucumber from your own main method
Creating an executable jar using Maven can be done in different ways. One way of doing it is described here:
http://www.thinkcode.se/blog/2011/03/05/create-an-executable-jar-from-maven
It is a small example that only focuses on executing something from a command line like this:
java -jar executable-example.jar
The example contains all dependencies. They are all bundled in the same jar. No need for any additional jars.
Next step would be to execute Cucumber from a main method. My approach would be to write a main that executes the Cucumber main method used for the command line version of Cucumber. The main method used to run cucumber from a command line lives in the cucumber-java library. You will find it at cucumber.api.cli.Main
Running a main method from another main method is done like this:
public static void main(String[] args) throws Throwable {
String[] arguments = {"foo", "bar"};
cucumber.api.cli.Main.main(arguments);
}
where arguments are the command line arguments you always want to execute Cucumber with.
Given these two steps, you should be able to execute Cucumber from your own executable jar wherever you are able to execute a jar at all.
Notice that you are mixing library version for Cucumber in your pom. I would use the latest version of all libraries. Compare cucumber-java, cucumber-testng and cucumber-junit. The latest Cucumber version is 1.2.4. I would use it for all of them.
More information about running Cucumber from a command line can be found here: https://cucumber.io/docs/cucumber/api/#from-the-command-line
I'd like to extend the accepted answer to support Gradle, since this might be helpful for someone.
Given this is your project's structure
.root
src/
main/
java/ --> Put your .java files here
CukesRunner.java --> This is your main file
resources
features/ --> Put your .feature files here
The build.gradle file should look like something like this:
apply plugin: 'java'
sourceSets {
main {
java {
srcDirs = ['src/main/java']
}
resources {
srcDirs = ['src/main/resources']
}
}
}
dependencies {
// Include your dependencies here
compile '...'
}
configurations {
cucumberRuntime {
extendsFrom compile
}
}
jar {
from {
// Package all dependencies in the .jar
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
manifest {
// Indicate the main class for the .jar file
attributes 'Main-Class': 'CukesRunner'
}
}
Then you can customize your CukesRunner.java just like the accepted answer suggested, but making sure to call for the features you compressed together with your jar file:
public class CukesRunner {
public static void main(String[] args) throws Throwable {
final String[] arguments = new String[]{
"--glue", "<my Java classes packages reference>",
"classpath:features" // This will look for the classpath inside the jar file
};
cucumber.api.cli.Main.main(arguments);
}
}
#SpringBootApplication
public class Application {
public static void main(final String[] args) throws Throwable {
// SpringApplication.run(TestApplication.class, args);
JUnitCore.main(CucumberTest.class.getCanonicalName());
}
}

No tests are executed while using junit Categories

I am trying to run tests based on categories but doesn't seem to work. I have spring boot application
Pom.xml is
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit47</artifactId>
<version>2.17</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
And I have defined categories as well
package com.in28minutes.springboot.controller;
public interface FastTests {
}
and junit is just to test
package com.in28minutes.springboot.controller;
import org.junit.Test;
import org.junit.experimental.categories.Category;
public class B {
#Category({ FastTests.class })
#Test
public void c() {
System.out.println("Hello \n\n\n\n\n");
}
}
but when I try to run maven command
mvn test -Dgroups="com.in28minutes.springboot.controller.FastTests"
no tests are executed. I assume it should execute test from class B.

How to scan maven plugin dependency?

In my project I attached commons-lang3 to plugin and commons-io to project:
<build>
<plugins>
<plugin>
<groupId>#project.groupId#</groupId>
<artifactId>#project.artifactId#</artifactId>
<version>#project.version#</version>
(...)
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
Within my custom plugin (within my mojo), I can find commons-io
#Component
private MavenProject project;
(...)
project.getDependecies(); // [{groupId=commons-io, artifactId=commons-io...
How to find commons-lang3?
Using the Maven API, from the MavenProject you can get the Build instance and from it then the list of configured plugins, from each Plugin object you can then have its dependencies as following:
#Component
private PluginDescriptor pluginDescriptor;
(...)
List<Plugin> plugins = project.getBuild().getPlugins();
for (Plugin p : plugins) {
if (p.getId().equals(pluginDescriptor.getId())) {
List<Dependency> pluginDependencies = p.getDependencies();
// your logic here
break;
}
}
If you really want to cover every case, you can also scan the plugins configured in the currently active profiles as following:
List<Profile> profiles = project.getActiveProfiles();
for (Profile p : profiles) {
// from personal experience, don't forget this check!
if (p.getBuild() != null) {
checkAsShownAbove(p.getBuild().getPlugins());
}
}
Hope that helps.
As commons-lang3 is declared as a dependency only for a specific plugin, it is not available during compilation. You have to explicitly define commons-lang3 as a dependency (if its not getting included as a transitive dependency) similar to how you have defined commons-io.

Resources