tests fat jar with maven shade-plugin - spring

I need to build one jar for execution tests during CI process on server. This jar have to contains all test dependencies (like junit, mockito) and spring boot dependencies while my test will use spring. I wrote such maven configuration:
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<id>shade</id>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>${project.artifactId}-fat</finalName>
<minimizeJar>false</minimizeJar>
<shadeTestJar>false</shadeTestJar>
<artifactSet>
<includes>
<include>*:*</include>
</includes>
</artifactSet>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>pl.klolo.demo.demo.smoke.SmokeTestAppKt</mainClass>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
Maven built one jar with all dependencies, but when I try to execute test I have strange error which I cannot understand:
2022-04-08 20:45:44.611 INFO 591618 --- [ main] p.k.d.demo.tests.DemoApplicationTests : No active profile set, falling back to 1 default profile: "default"
2022-04-08 20:45:45.018 ERROR 591618 --- [ main] o.s.boot.SpringApplication : Application run failed
java.lang.IllegalStateException: Unable to read meta-data for class
at org.springframework.boot.autoconfigure.AutoConfigurationSorter$AutoConfigurationClass.getAnnotationMetadata(AutoConfigurationSorter.java:237) ~[demo-fat.jar:0.0.1]
at org.springframework.boot.autoconfigure.AutoConfigurationSorter$AutoConfigurationClass.getOrder(AutoConfigurationSorter.java:208) ~[demo-fat.jar:0.0.1]
at org.springframework.boot.autoconfigure.AutoConfigurationSorter$AutoConfigurationClass.access$000(AutoConfigurationSorter.java:154) ~[demo-fat.jar:0.0.1]
at org.springframework.boot.autoconfigure.AutoConfigurationSorter.lambda$getInPriorityOrder$0(AutoConfigurationSorter.java:64) ~[demo-fat.jar:0.0.1]
at java.base/java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) ~[na:na]
at java.base/java.util.TimSort.sort(TimSort.java:220) ~[na:na]
at java.base/java.util.Arrays.sort(Arrays.java:1515) ~[na:na]
at java.base/java.util.ArrayList.sort(ArrayList.java:1750) ~[na:na]
at org.springframework.boot.autoconfigure.AutoConfigurationSorter.getInPriorityOrder(AutoConfigurationSorter.java:62) ~[demo-fat.jar:0.0.1]
Could you tell me what is wrong and how to fix it?
SmokeTestApp:
fun main(args: Array<String>) {
val launcher = LauncherFactory.create()
val listener = SummaryGeneratingListener()
launcher.registerTestExecutionListeners(listener)
launcher.execute(
LauncherDiscoveryRequestBuilder.request()
.selectors(selectPackage("pl.klolo"))
.build()
)
printSummary(listener.summary)
exitProcess(
if (listener.summary.failures.isEmpty()) 0
else -1
)
}
fun printSummary(summary: TestExecutionSummary) {
println("Passed tests: ${summary.testsSucceededCount}")
summary.failures.forEach {
println("###FAILED TEST ${it.testIdentifier.uniqueId}###")
it.exception.printStackTrace()
}
}
DemoApplicationTests:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class DemoApplicationTests(
#Value("\${test.applicationURI}") val applicationURI: String,
#Value("\${test.applicationPort}") val applicationPort: Int
) {
init {
RestAssured.baseURI = applicationURI
RestAssured.port = applicationPort
}
#Tag("smoke_test")
#Test
fun contextLoads() {
println("smoke test ===> 1")
}

I found reason of my problem. It was file spring.factories with entry:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
When I removed file jar works correctly

Related

OpenAPI Codegen "oneOf": Java classes not generated correctly

TLDR:
OpenAPI offers 'oneOf' property. The resulting java classes created do not seem to allow one of the possible instances.
Details:
I am creating the spring/java server side rest api code using OpenAPI maven plugin.
The request class is not such that the passed object is not parsed correctly. The following error is printed in the console.
JSON parse error: Could not resolve subtype of [simple type, class com.model.Issuer]: missing type id property 'type' (for POJO property 'issuer'); nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class com.Issuer]: missing type id property 'type' (for POJO property 'issuer')\n at [Source: (PushbackInputStream); line: 3, column: 15] (through reference chain: com.IssueCredentialRequest[\"credential\"]->com.Credential[\"issuer\"])
The reason seem to be that the generated class does not have subtypes list in the annotation:
package com.sphereon.vdp.vc.service.model;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
})
public interface OneOfIssuer {
}
There is another class which is generated correctly. The reason that this one is generated correctly is probably that this one deals with non-primitive types.
package com.sphereon.vdp.vc.service.model;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = VerifyPresentationRequest.class, name = "VerifyPresentationRequest"),
#JsonSubTypes.Type(value = ProoflessVerifyPresentationRequest.class, name = "ProoflessVerifyPresentationRequest")
})
public interface OneOfpresentationsVerifyBody {
}
Can someone point to how to fix the code generation for primitive types?
<plugin>
<groupId>io.swagger.codegen.v3</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>3.0.33</version>
<dependencies>
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars</artifactId>
<version>4.3.0</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>vc-rest-api-issuer-source-generation</id>
<goals>
<goal>generate</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<inputSpec>${pom.basedir}/specifications/issuer.yml</inputSpec>
<language>spring</language>
<apiPackage>com.company.vdp.vc.service.api</apiPackage>
<modelPackage>com.company.vdp.vc.service.model</modelPackage>
<artifactVersion>${project.version}</artifactVersion>
<generateModels>true</generateModels>
<generateApis>true</generateApis>
<generateModelDocumentation>true</generateModelDocumentation>
<generateSupportingFiles>true</generateSupportingFiles>
<verbose>${openapi-codegen-verbose}</verbose>
<output>${project.basedir}/target/generated-sources/java/api</output>
<ignoreFileOverride>${project.basedir}/target/generated-sources/java/api/.swagger-codegen-ignore</ignoreFileOverride>
<configOptions>
<delegatePattern>true</delegatePattern>
<dateLibrary>java8</dateLibrary>
<useTags>true</useTags>
</configOptions>
</configuration>
</execution>
<execution>
<id>vc-rest-api-verifier-source-generation</id>
<goals>
<goal>generate</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<inputSpec>${pom.basedir}/specifications/verifier.yml</inputSpec>
<language>spring</language>
<apiPackage>com.company.vdp.vc.service.api</apiPackage>
<modelPackage>com.company.vdp.vc.service.model</modelPackage>
<artifactVersion>${project.version}</artifactVersion>
<generateModels>true</generateModels>
<generateApis>true</generateApis>
<generateModelDocumentation>true</generateModelDocumentation>
<generateSupportingFiles>true</generateSupportingFiles>
<verbose>${openapi-codegen-verbose}</verbose>
<output>${project.basedir}/target/generated-sources/java/api</output>
<ignoreFileOverride>${project.basedir}/target/generated-sources/java/api/.swagger-codegen-ignore</ignoreFileOverride>
<configOptions>
<delegatePattern>true</delegatePattern>
<dateLibrary>java8</dateLibrary>
<useTags>true</useTags>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
Following is the spec file that I am using without editing.
https://github.com/w3c-ccg/vc-api/blob/main/components/Issuer.yml
https://github.com/w3c-ccg/vc-api/blob/main/verifier.yml

Apache Camel SOAP/CXF request to Server (Spring Boot)

I am trying to make requests to dedicated WSDL server with the help of Apache Camel CXF.
I have the WSDL URL:
http://www.learnwebservices.com/services/tempconverter?wsdl
I've made the Java classes of WSDL using the cxf-codegen-plugin:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>3.4.2</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>wsdl2java</goal>
</goals>
<configuration>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/wsdl/tempconverter.wsdl</wsdl>
<packagenames>
<packagename>office.planet.integrations.merlion</packagename>
</packagenames>
</wsdlOption>
</wsdlOptions>
</configuration>
</execution>
</executions>
</plugin>
I have the following Camel route:
#Component
public class MerlionRoute
extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:celsius-to-fahrenheit")
.process(exchange -> {
System.out.println("HELLO!!!!!");
CelsiusToFahrenheitRequest c = new CelsiusToFahrenheitRequest();
c.setTemperatureInCelsius(Double.valueOf(exchange.getIn().getHeader("num").toString()));
exchange.getIn().setBody(c);
})
.setHeader(CxfConstants.OPERATION_NAME, constant("CelsiusToFahrenheit"))
.setHeader(CxfConstants.OPERATION_NAMESPACE, constant("{{endpoint.namespace}}"))
.to("cxf:bean:cxfConvertTemp")
.process(exchange -> {
System.out.println("WE ARE HERE");
MessageContentsList response = (MessageContentsList) exchange.getIn().getBody();
CelsiusToFahrenheitResponse r = (CelsiusToFahrenheitResponse) response.get(0);
exchange.getIn().setBody("Temp in Farenheit: "+r.getTemperatureInFahrenheit());
System.out.println(r.getTemperatureInFahrenheit());
})
.end();
}
}
The Bean class of the EndPoint:
#Configuration
public class CxfBeans {
#Value("${endpoint.wsdl}")
private String SOAP_URL;
#Bean(name = "cxfConvertTemp")
public CxfEndpoint buildCxfEndpoint() {
CxfEndpoint cxf = new CxfEndpoint();
cxf.setAddress(SOAP_URL);
cxf.setServiceClass(TempConverterEndpoint.class);
cxf.setWsdlURL(SOAP_URL);
return cxf;
}
}
And the WSDL endpoint:
endpoint.wsdl=http://www.learnwebservices.com/services/tempconverter?wsdl
endpoint.namespace=http://learnwebservices.com/services/tempconverter
When I am launching the project, my route starts, but nothing happens.
Only this I can see:
2022-03-31 18:41:44.933 INFO 44313 --- [ restartedMain] o.a.c.w.s.f.ReflectionServiceFactoryBean : Creating Service {http://learnwebservices.com/services/tempconverter}TempConverterEndpointService from WSDL: http://www.learnwebservices.com/services/tempconverter?wsdl
How shall I request the data from WSDL server within the Camel CXF? What am I doing wrong?
Your route needs to be triggered. As it stands, nothing calls your from endpoint "direct:celsius-to-fahrenheit", and thus indeed, nothing happens.
Assuming you want this route to be triggered only once, you could define your from endpoint as "timer://celsius-to-fahrenheit?repeatCount=1".
See Camel Timer component.

Setting descriptions for mojos and parameters in maven plugin

I am writing a maven plugin and I would like to add some documentation for the available goals and parameters.
When I run mvn help:describe -Dplugin=myplugin -Ddetail it prints out available goals and parameters. However it lists (no description) everywhere. From searching the internet I could not figure out where such a description is to be set.
For reference, my plugin is written in scala and looks roughly like this.
import org.apache.maven.plugins.annotations.{ Component, Parameter }
class MyMojo extends AbstractMojo {
#Parameter(defaultValue = "false", readonly = false)
private var skipFormatting: Boolean = _
}
So my question would be: where can set a description, such that it will show up with mvn help:describe -Dplugin=myplugin?
I strongly recommend to use the following:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>default-descriptor</id>
<phase>process-classes</phase>
</execution>
<execution>
<id>generate-helpmojo</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>
which will generate the help part during the build process. And yes you have to add some javadoc like this:
#Mojo(name = "failure", defaultPhase = LifecyclePhase.NONE,
requiresDependencyResolution = ResolutionScope.NONE, threadSafe = true)
public class FailureMojo extends AbstractMojo {
I don't understand why you haven't have any annotations on your Mojo?
The documentation like this: https://maven.apache.org/plugins/maven-install-plugin/plugin-info.html will be generated from the javadoc on parameters etc. https://github.com/apache/maven-install-plugin/blob/master/src/main/java/org/apache/maven/plugins/install/InstallMojo.java#L69
Based on examples when running the help:describe on familiar plugins like the maven-jar-plugin, tt appears it's based on the Javadoc of the Mojo class.

How to precompile jsp in a spring boot application?

I'm using Spring boot and we were using Spring with Tomcat before that.
When we used Spring and Tomcat two years ago, we used a maven plugin to precompile the jsp.
It was really useful to avoid this compilation to be made for every first visits after a deployement.
However all maven plugin that we know dumps a web.xml file that list all jsp and associated generated servlets.
With Spring boot, it don't use web.xml anymore, so this file is ignored.
We still have the compilation and that's a security belt but there is a penalty for every first visit on each page.
Does anybody know if it's possible to precompile jsp in a Spring boot application ?
I got precompiling to work either at server start time (don't have to use JspC, so simpler build file) and at build time (much quicker server start time). I register the resulting servlets dynamically, so you don't have to manually change any files if you add/remove JSPs.
At server start time
Use ServletRegistration.Dynamic to register a JSP_SERVLET_CLASS Servlet for each JSP.
Use the initParameter jspFile to set the JSP filename (ref)
e.g. for SpringBoot in a ServletContextInitializer (ref):
#Bean
public ServletContextInitializer preCompileJspsAtStartup() {
return servletContext -> {
getDeepResourcePaths(servletContext, "/WEB-INF/jsp/").forEach(jspPath -> {
log.info("Registering JSP: {}", jspPath);
ServletRegistration.Dynamic reg = servletContext.addServlet(jspPath, Constants.JSP_SERVLET_CLASS);
reg.setInitParameter("jspFile", jspPath);
reg.setLoadOnStartup(99);
reg.addMapping(jspPath);
});
};
}
private static Stream<String> getDeepResourcePaths(ServletContext servletContext, String path) {
return (path.endsWith("/")) ? servletContext.getResourcePaths(path).stream().flatMap(p -> getDeepResourcePaths(servletContext, p))
: Stream.of(path);
}
At build time
Generate Java source files for each JSP and a web.xml with their servlet mappings using JspC (ref).
Then register these with the ServletContext (by parsing the web.xml with Tomcat's WebXmlParser, e.g. for SpringBoot:
#Value("classpath:precompiled-jsp-web.xml")
private Resource precompiledJspWebXml;
#Bean
public ServletContextInitializer registerPreCompiledJsps() {
return servletContext -> {
// Use Tomcat's web.xml parser (assume complete XML file and validate).
WebXmlParser parser = new WebXmlParser(false, true, true);
try (InputStream is = precompiledJspWebXml.getInputStream()) {
WebXml webXml = new WebXml();
boolean success = parser.parseWebXml(new InputSource(is), webXml, false);
if (!success) {
throw new RuntimeException("Error parsing Web XML " + precompiledJspWebXml);
}
for (ServletDef def : webXml.getServlets().values()) {
log.info("Registering precompiled JSP: {} = {} -> {}", def.getServletName(), def.getServletClass());
ServletRegistration.Dynamic reg = servletContext.addServlet(def.getServletName(), def.getServletClass());
reg.setLoadOnStartup(99);
}
for (Map.Entry<String, String> mapping : webXml.getServletMappings().entrySet()) {
log.info("Mapping servlet: {} -> {}", mapping.getValue(), mapping.getKey());
servletContext.getServletRegistration(mapping.getValue()).addMapping(mapping.getKey());
}
} catch (IOException e) {
throw new RuntimeException("Error registering precompiled JSPs", e);
}
};
}
Example Maven config to generate and compile the JSP classes, and generate the precompiled-jsp-web.xml:
<!-- Needed to get the jasper Ant task to work (putting it in the plugin's dependencies didn't work) -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina-ant</artifactId>
<version>8.0.32</version>
<scope>provided</scope>
</dependency>
<!-- ... -->
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>precompile-jsp-generate-java</id>
<!-- Can't be generate-sources because we need the compiled Henry taglib classes already! -->
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo message="Precompiling JSPs"/>
<property name="compile_classpath" refid="maven.compile.classpath"/>
<property name="target_dir" value="${project.basedir}/generated-sources/jspc" />
<path id="jspc_classpath">
<path path="${compile_classpath}"/>
</path>
<typedef resource="org/apache/catalina/ant/catalina.tasks" classpathref="jspc_classpath"/>
<mkdir dir="${target_dir}/java"/>
<mkdir dir="${target_dir}/resources"/>
<jasper
validateXml="false"
uriroot="${project.basedir}/src/main/webapp"
compilertargetvm="1.8"
compilersourcevm="1.8"
failonerror="true"
javaencoding="UTF-8"
webXml="${target_dir}/resources/precompiled-jsp-web.xml"
outputDir="${target_dir}/java/" >
</jasper>
<!-- Can't use Maven to compile the JSP classes because it has already compiled the app's classes
(needed to do that becuase JspC needs compiled app classes) -->
<javac srcdir="${target_dir}/java" destdir="${project.build.outputDirectory}" classpathref="jspc_classpath" fork="true"/>
<!-- Have to copy the web.xml because process-resources phase has already finished (before compile) -->
<copy todir="${project.build.outputDirectory}">
<fileset dir="${target_dir}/resources"/>
</copy>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
<!-- Not strictly necessary, because Ant does the compilation, but at least attempts to keep it in sync with Maven -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-precompiled-jsp-java-sources</id>
<phase>generate-sources</phase>
<goals><goal>add-source</goal></goals>
<configuration>
<sources>
<source>${project.basedir}/generated-sources/jspc/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-precompiled-jsp-resources</id>
<phase>generate-resources</phase>
<goals><goal>add-resource</goal></goals>
<configuration>
<resources>
<resource>
<directory>${project.basedir}/generated-sources/jspc/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
Based on the excellent answer of paulcm I came up with my own solution as the above solution didn't work for me and I couldn't track down the error. Maybe the answer above is outdated for tomcat9. Or it had some problem with multi-module setup. However: All credits belong to paulcm
This is only the compile time solution.
Add these two plugins to your pom.xml
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jspc-maven-plugin</artifactId>
<version>9.4.15.v20190215</version>
<executions>
<execution>
<id>jspc</id>
<goals>
<goal>jspc</goal>
</goals>
<configuration>
<mergeFragment>true</mergeFragment>
<sourceVersion>1.8</sourceVersion>
<targetVersion>1.8</targetVersion>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webXml>${project.basedir}/target/web.xml</webXml>
</configuration>
</plugin>
Add an empty web.xml file
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
<session-config>
<cookie-config>
</cookie-config>
</session-config>
</web-app>
Add a Registry
import org.apache.tomcat.util.descriptor.web.ServletDef;
import org.apache.tomcat.util.descriptor.web.WebXml;
import org.apache.tomcat.util.descriptor.web.WebXmlParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.xml.sax.InputSource;
import javax.servlet.ServletRegistration;
import java.io.InputStream;
import java.util.Map;
#Configuration
public class PreCompileJspRegistry {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Bean
public ServletContextInitializer registerPreCompiledJsps() {
return servletContext -> {
InputStream inputStream = servletContext.getResourceAsStream("/WEB-INF/web.xml");
if (inputStream == null) {
logger.info("Could not read web.xml");
return;
}
try {
WebXmlParser parser = new WebXmlParser(false, false, true);
WebXml webXml = new WebXml();
boolean success = parser.parseWebXml(new InputSource(inputStream), webXml, false);
if (!success) {
logger.error("Error registering precompiled JSPs");
}
for (ServletDef def : webXml.getServlets().values()) {
logger.info("Registering precompiled JSP: {} = {} -> {}", def.getServletName(), def.getServletClass());
ServletRegistration.Dynamic reg = servletContext.addServlet(def.getServletName(), def.getServletClass());
reg.setLoadOnStartup(99);
}
for (Map.Entry<String, String> mapping : webXml.getServletMappings().entrySet()) {
logger.info("Mapping servlet: {} -> {}", mapping.getValue(), mapping.getKey());
servletContext.getServletRegistration(mapping.getValue()).addMapping(mapping.getKey());
}
} catch (Exception e) {
logger.error("Error registering precompiled JSPs", e);
}
};
}
}
A comment for "At server start time" outlined above: the servlet you create will by default be in development mode if the application is packaged in an executable jar, so you if you use it in production mode, you should also set development = false ++ to prevent the jsps from being compiled again:
reg.setInitParameter("genStringAsCharArray", "true");
reg.setInitParameter("trimSpaces", "true");
reg.setInitParameter("development", "false");

gwt-test-utils unit fail when run with jacoco

We are attempting to generate coverage reports for our GWT application from a set of unit tests written using gwt-test-utils. The project is a multi-module maven project. We are using the sonar-plugin on jenkins in order to generate and collate our coverage and violation information.
When the build jobs run all the GWT unit tests pass as part of the normal build, however when the Sonar plugin attempts to rerun the tests they all fail with the following error:
initializationError(uk.co.card.gwt.retailpost.client.dialog.productmodify.CurrencyEditDialogTest) Time elapsed: 0 sec <<< ERROR!
com.googlecode.gwt.test.exceptions.GwtTestException: Error while generating gwt-test-utils prerequisites
at com.googlecode.gwt.test.internal.GwtFactory.(GwtFactory.java:113)
at com.googlecode.gwt.test.internal.GwtFactory.initializeIfNeeded(GwtFactory.java:45)
at com.googlecode.gwt.test.internal.junit.AbstractGwtRunner.(AbstractGwtRunner.java:30)
at com.googlecode.gwt.test.GwtRunner.(GwtRunner.java:19)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:29)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:21)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:26)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:250)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)
at com.google.gwt.dev.cfg.ModuleDef.checkForSeedTypes(ModuleDef.java:559)
at com.google.gwt.dev.cfg.ModuleDef.getCompilationState(ModuleDef.java:363)
at com.google.gwt.dev.cfg.ModuleDef.getCompilationState(ModuleDef.java:354)
at com.googlecode.gwt.test.internal.GwtFactory.createCompilationState(GwtFactory.java:151)
at com.googlecode.gwt.test.internal.GwtFactory.(GwtFactory.java:106)
... 25 more
Looking through the rest of the console output from jenkins, and the workspace directories, I can't locate the log file that the "com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)" refers to.
Has anyone encountered a similar problem and knows how to get Sonar to run the gwt-test-utils successfully, or at least will have an idea when to look to find the previous log entries mentioned in the exception.
EDIT: After further experimentation the issue appears to be being caused by jacoco. trying to run just the unit tests instrumented with jacoco (and with no sonar involved) results in the same error
**EDIT:
sample from pom.xml
<build>
pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.6.2.201302030002</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<excludedGroups combine.self="override" />
<reuseForks>true</reuseForks>
<argLine>-Xmx1024m -XX:MaxPermSize=256m ${jacoco.agent.argLine}</argLine>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<propertyName>jacoco.agent.argLine</propertyName>
<destFile>${sonar.jacoco.itReportPath}</destFile>
<append>true</append>
<excludes>
<exclude>*.osgi.*</exclude>
<exclude>*.apache.*</exclude>
<exclude>*.sourceforge.*</exclude>
<exclude>*.junit.*</exclude>
<!-- Test support code does not need to be covered -->
<exclude>uk.co.card.retailpost.clientintegration.utilities.*</exclude>
</excludes>
<classDumpDir>temp/classes</classDumpDir>
</configuration>
<executions>
<execution>
<id>agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
As I mentioned in comments libraries are loaded in different order for jacoco with maven-surefire-plugin. To solve this problem write your own runner (extends com.googlecode.gwt.test.GwtRunner) and change classloader for thread contextClassLoader.
import com.googlecode.gwt.test.GwtRunner;
public class MyGwtRunner extends GwtRunner {
static {
URLClassLoader classLoader = (URLClassLoader) MyGwtRunner.class.getClassLoader();
try {
URL[] urls = getClassPath();
ClassLoader cl = URLClassLoader.newInstance(urls, classLoader);
Thread.currentThread().setContextClassLoader(cl);
} catch (MalformedURLException e) {
throw new IllegalStateException(e);
}
}
public MyGwtRunner(Class<?> clazz) throws Throwable {
super(clazz);
}
private static URL[] getClassPath() throws MalformedURLException {
String classPath = System.getProperty("java.class.path");
String pathSeparator = System.getProperty("path.separator");
String[] array = classPath.split(pathSeparator);
List<URL> files = new ArrayList<URL>();
for (String a : array) {
files.add(new File(a).toURI().toURL());
}
return files.toArray(new URL[files.size()]);
}
}
In your tests override GwtRunner by MyGwtRunner
#GwtModule("com.my.module.GwtTestUtils")
#RunWith(MyGwtRunner.class)
public abstract class AbstractGwtJunit extends GwtTest {
....
}

Resources