I m new to Spock, tried to write a simple Spock but it failed:
Error:Groovyc: Could not instantiate global transform class org.spockframework.compiler.SpockTransform specified at jar:file:.../.m2/repository/org/spockframework/spock-core/1.0-groovy-2.4/spock-core-1.0-groovy-2.4.jar!/META-INF/services/org.codehaus.groovy.transform.ASTTransformation because of exception java.lang.NullPointerException
here is my Spec:
#Unroll
class UserFilterSpec extends Specification {
private static final String USER_ID_1 = "someUserId1";
private static final String USER_ID_2 = "someUserId2";
private static final String USER_ID_3 = "someUserId3";
def filter = new UserFilter()
def User user1 = setupTestUser(USER_ID_1)
def User user2 = setupTestUser(USER_ID_2)
def User user3 = setupTestUser(USER_ID_3)
def "given a list of users and list of user ids, should return list of user with those users removed"() {
expect:
filter.filterUserDataByIds(userList, userIdList) == filterList
where:
userList | userIdList || filterList
Lists.newArrayList(user1, user2, user3) | Lists.newArrayList(USER_ID_1) || Lists.newArrayList(user2, user3)
}
}
and here is my pom.xml:
<!-- Mandatory plugins for using Spock -->
<plugin>
<!-- The gmavenplus plugin is used to compile Groovy code. To learn more about this plugin,
visit https://github.com/groovy/GMavenPlus/wiki -->
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Optional plugins for using Spock -->
<!-- Only required if names of spec classes don't match default Surefire patterns (`*Test` etc.) -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
<configuration>
<useFile>false</useFile>
<includes>
<include>**/*Spec.java</include>
<include>**/*Test.java</include> <!-- Just in case of having also "normal" JUnit tests -->
</includes>
</configuration>
</plugin>
...
<!-- Mandatory dependencies for using Spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.0-groovy-2.4</version>
<scope>test</scope>
</dependency>
<!-- Optional dependencies for using Spock -->
<dependency> <!-- use a specific Groovy version rather than the one specified by spock-core -->
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.1</version>
</dependency>
<dependency> <!-- enables mocking of classes (in addition to interfaces) -->
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>
<dependency> <!-- enables mocking of classes without default constructor (together with CGLIB) -->
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>2.1</version>
<scope>test</scope>
</dependency>
what is wrong my Spec or pom setting? do i have to install Groovy to make it work?
Here is your test, re-written in more idiomatic Spock/Groovy:
#Unroll
class UserFilterSpec extends Specification {
static final String USER_ID_1 = "someUserId1"
static final String USER_ID_2 = "someUserId2"
static final String USER_ID_3 = "someUserId3"
#Shared user1 = setupTestUser(USER_ID_1)
#Shared user2 = setupTestUser(USER_ID_2)
#Shared user3 = setupTestUser(USER_ID_3)
#Shared filter = new UserFilter()
def "given a list of users and list of user ids, should return list of user with those users removed"() {
expect:
filter.filterUserDataByIds(userList, userIdList) == filterList
where:
userList | userIdList || filterList
[user1, user2, user3] | [USER_ID_1] || [user2, user3]
}
}
A couple of notes:
Groovy (in which Spock tests are written) has native support for declaring collections. Thus [user1, user3] instead of Lists.newArrayList.
#Shared or static is required for class-level variables that are used in tests. This will solve your compilation problem.
I highly recommend reading the Groovy user guide, it's a great read and will help you immensely getting started with Spock. The style guide is here: http://www.groovy-lang.org/style-guide.html, and from there you can explore the Documentation page for other interesting tidbits.
The Spock documentation is also excellent, and can easily be read in one sitting: http://docs.spockframework.org.
Add static or #Shared for non-method variables - 'filter', 'user 1-3'.
You can`t access non static class properties from inside the 'where' section.
In my case I solved the groovy error by upgrading from spring boot 2.3.4 to 2.5.3
https://issueexplorer.com/issue/spockframework/spock-example/44
Also, try upgrading from 1.3-groovy-2.5 to 2.0-groovy-3.0
Related
I'm trying to generate a service that has enum as query parameter, but it keeps generating it wrong.
Here's the part of yaml:
name: language
in: query
description: language
schema:
type: string
enum:
- en
- de
and I'm using it in parameters:
- $ref: '#/components/parameters/language'
The error that I get is:
[ERROR] .../api/TestApi.java:[110,65] illegal start of type
[ERROR] .../api/TestApi.java:[110,66] ')' expected
[ERROR] .../api/TestApi.java:[110,82] ';' expected
Here's what the code looks like:
public Response getByLanguage( #PathParam("id") Long id, , allowableValues="en, de" #QueryParam("language") String language
And here's my plugin:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>5.0.1</version>
<configuration>
<inputSpec>${basedir}/src/main/resources/openapi.yaml</inputSpec>
<output>${project.build.directory}/generated-sources/java</output>
<generatorName>jaxrs-resteasy-eap</generatorName>
<modelPackage>com.openapi.example.model</modelPackage>
<apiPackage>com.openapi.example.api</apiPackage>
<generateSupportingFiles>false</generateSupportingFiles>
<configOptions>
<sourceFolder>openapi</sourceFolder>
<dateLibrary>java8</dateLibrary>
<interfaceOnly>true</interfaceOnly>
<java8>true</java8>
<serializableModel>true</serializableModel>
<useTags>true</useTags>
<performBeanValidation>true</performBeanValidation>
<useBeanValidation>true</useBeanValidation>
</configOptions>
</configuration>
<executions>
<execution>
<phase>generate-resources</phase>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
The only way I've managed to get it to work is to make it as an array of enum values, but that's not what I need here.
EDIT:
dependencies I have defined in the project:
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-dependency-analyzer</artifactId>
<version>1.11.1</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.5.8</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.1.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.11.3</version>
</dependency>
Multiple long standing enum bugs exist in the generators (for all languages as far as I can tell).
For example https://github.com/OpenAPITools/openapi-generator/issues/4300
But also .. https://github.com/OpenAPITools/openapi-generator/issues/2645
Try out with below demo yaml.
Enums should be defined as enum: [en, de].
openapi: 3.0.2
info: {title: Demo Info., description: Demo Description., version: 1.0.0}
servers:
- {url: 'https://something.com', description: Demo Description.}
paths:
/something:
post:
tags: [Something]
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/SomethingRequest'}
parameters:
- {$ref: '#/components/parameters/language'}
responses:
200:
description: OK
content:
application/json:
schema: {$ref: '#/components/schemas/SomethingResponse'}
components:
parameters:
language:
name: language
schema:
type: string
enum: [en, de]
default: en
in: query
schemas:
SomethingRequest:
properties:
demo: {type: string}
SomethingResponse:
properties:
demo: {type: string}
It depends what generator you are using but jaxrs-resteasy-eap is broken when using enums in query parameters.
I think this commit is causing the problem.
As a temporary fix in my project I edited the queryParams.mustache template file to as this:
{{#isQueryParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{^isContainer}}{{#allowableValues}}#ApiParam(value = "{{{description}}}"{{#required}},required=true{{/required}}, {{> allowableValues }}){{/allowableValues}}{{#defaultValue}} #DefaultValue("{{{.}}}"){{/defaultValue}}{{/isContainer}} #QueryParam("{{baseName}}") {{{dataType}}} {{paramName}}{{/isQueryParam}}
Just noticed that there are also github issue of this. The recommendation of removing the allowableValues from queryParams.mustache might be the correct way to go:
{{#isQueryParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{^isContainer}}{{#defaultValue}} #DefaultValue("{{{.}}}"){{/defaultValue}}{{/isContainer}} #QueryParam("{{baseName}}") {{{dataType}}} {{paramName}}{{/isQueryParam}}
It depends how you are using the generator how to use own templates.
I am using it from npm #openapitools/openapi-generator-cli
There you can apply your own templates by adding -t/--template-dir option into your generate command. Here is mine as example:
npx #openapitools/openapi-generator-cli generate -t=templates/jaxrs -i openapi.yaml -o generated/jaxrs -g jaxrs-resteasy-eap -c config-jaxrs.yaml
I just copied my modified queryParams.mustache to root of templates/jaxrs and the generated api functions did not have double comma syntax problems anymore:
public Response getPets( #ApiParam(value = "My param description", allowableValues="AA, BB, CC") #QueryParam("petType") PetType petType,#Context SecurityContext securityContext);
I haven't used these files yet, so I don't know if everything is correct, but atleast the syntax is correct now.
More info here: templating documentation
I am trying to test out some code that works with Java Doc, it is used under the maven-javadoc-plugin. I am trying to get it to work under jdk11. I am after an implementation of RootDoc which I can use when running tests.
Currently the tests use EasyDoclet which gives me a RootDoc like so:
EasyDoclet easyDoclet = new EasyDoclet(new File("dir"), "com.foo.bar");
RootDoc rootDoc = easyDoclet.getRootDoc()
However I could not get this to work under jdk11.
The first issue I had was tools.jar is missing so I changed my pom.xml to have:
<dependency>
<groupId>org.seamless</groupId>
<artifactId>seamless-javadoc</artifactId>
<version>1.1.1</version>
<exclusions>
<exclusion>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- maybe this will get what ever was in tools.jar -->
<dependency>
<groupId>com.github.olivergondza</groupId>
<artifactId>maven-jdk-tools-wrapper</artifactId>
<version>0.1</version>
</dependency>
This lead to many instances of:
java.lang.NoClassDefFoundError: com/sun/tools/javadoc/PublicMessager
The PublicMessager class seems to exist to make public some constructors, I am not sure why it exists under the com.sun.tools package. I tried to make a copy of this class:
public static class PublicMessager extends
com.sun.tools.javadoc.main.Messager {
public PublicMessager(Context context, String s) {
super(context, s);
}
public PublicMessager(Context context, String s, PrintWriter printWriter, PrintWriter printWriter1, PrintWriter printWriter2) {
super(context, s, printWriter, printWriter1, printWriter2);
}
}
And the error message changes to:
java.lang.IllegalAccessError: superclass access check failed: class com.fun.javadoc.FooBar$PublicMessager (in unnamed module #0x4abdb505) cannot access class com.sun.tools.javadoc.main.Messager (in module jdk.javadoc) because module jdk.javadoc does not export com.sun.tools.javadoc.main to unnamed module #0x4abdb50
I exposed jdk.javadoc to the unnamed module using:
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
<argLine>--add-opens=jdk.javadoc/com.sun.tools.javadoc.main=ALL-UNNAMED</argLine>
</configuration>
</plugin>
</plugins>
</build>
This meant that my custom version of PublicMessager would no longer have the errors shown however the version from seamless under com.sun.tools could not be found. I made my own version of EasyDoclet which used my PublicMessager however it turned out that the following two classes are missing:
import com.sun.tools.javadoc.JavadocTool;
import com.sun.tools.javadoc.ModifierFilter;
At this point I am not sure what to do. halp!
Perhaps an alternative would be to instead find the jdk11 equivalent of RootDoc which I think is DocletEnvironment and then some how get an implementation of that, I have no idea how to get an implementation of DocletEnvironment.
I would like to introduce Byte Buddy to my company and I have prepared a demo for my colleagues. Since we use Spring a lot, I thought the best example would be instrumentation of SpringBoot application. I have decided to add logs to RestController methods.
Instrumented application is a simple SpringBoot Hello World example:
#RestController
public class HelloController {
private static final String template = "Hello, %s!";
#RequestMapping("/hello")
public String greeting(
#RequestParam(value = "name", defaultValue = "World") String name) {
return String.format(template, name);
}
#RequestMapping("/browser")
public String showUserAgent(HttpServletRequest request) {
return request.getHeader("user-agent");
}
}
And here is my Byte Buddy agent:
public class LoggingAgent {
public static void premain(String agentArguments,
Instrumentation instrumentation) {
install(instrumentation);
}
public static void agentmain(String agentArguments,
Instrumentation instrumentation) {
install(instrumentation);
}
private static void install(Instrumentation instrumentation) {
createAgent(RestController.class, "greeting")
.installOn(instrumentation);
}
private static AgentBuilder createAgent(
Class<? extends Annotation> annotationType, String methodName) {
return new AgentBuilder.Default().type(
ElementMatchers.isAnnotatedWith(annotationType)).transform(
new AgentBuilder.Transformer() {
#Override
public DynamicType.Builder<?> transform(
DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassLoader classLoader) {
return builder
.method(ElementMatchers.named(methodName))
.intercept(
MethodDelegation
.to(LoggingInterceptor.class)
.andThen(
SuperMethodCall.INSTANCE));
}
});
}
}
Interceptor logs method execution:
public static void intercept(#AllArguments Object[] allArguments,
#Origin Method method) {
Logger logger = LoggerFactory.getLogger(method.getDeclaringClass());
logger.info("Method {} of class {} called", method.getName(), method
.getDeclaringClass().getSimpleName());
for (Object argument : allArguments) {
logger.info("Method {}, parameter type {}, value={}",
method.getName(), argument.getClass().getSimpleName(),
argument.toString());
}
}
When executed with -javaagent parameter this example works well. When however I try to load the agent on the running JVM with Attach API:
VirtualMachine vm = VirtualMachine.attach(args[0]);
vm.loadAgent(args[1]);
vm.detach();
I've got the following exception on the first logging attempt:
Exception in thread "ContainerBackgroundProcessor[StandardEngine[Tomcat]]" java.lang.IncompatibleClassChangeError: Class ch.qos.logback.classic.spi.ThrowableProxy does not implement the requested interface ch.qos.logback.classic.spi.IThrowableProxy
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.subjoinExceptionMessage(ThrowableProxyConverter.java:180)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.subjoinFirstLine(ThrowableProxyConverter.java:176)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.recursiveAppend(ThrowableProxyConverter.java:159)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.throwableProxyToString(ThrowableProxyConverter.java:151)
at org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter.throwableProxyToString(ExtendedWhitespaceThrowableProxyConverter.java:35)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.convert(ThrowableProxyConverter.java:145)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.convert(ThrowableProxyConverter.java:1)
at ch.qos.logback.core.pattern.FormattingConverter.write(FormattingConverter.java:36)
at ch.qos.logback.core.pattern.PatternLayoutBase.writeLoopOnConverters(PatternLayoutBase.java:114)
at ch.qos.logback.classic.PatternLayout.doLayout(PatternLayout.java:141)
at ch.qos.logback.classic.PatternLayout.doLayout(PatternLayout.java:1)
at ch.qos.logback.core.encoder.LayoutWrappingEncoder.doEncode(LayoutWrappingEncoder.java:130)
at ch.qos.logback.core.OutputStreamAppender.writeOut(OutputStreamAppender.java:187)
at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:212)
at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:100)
at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:84)
at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:48)
at ch.qos.logback.classic.Logger.appendLoopOnAppenders(Logger.java:270)
at ch.qos.logback.classic.Logger.callAppenders(Logger.java:257)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:421)
at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:383)
at ch.qos.logback.classic.Logger.log(Logger.java:765)
at org.slf4j.bridge.SLF4JBridgeHandler.callLocationAwareLogger(SLF4JBridgeHandler.java:221)
at org.slf4j.bridge.SLF4JBridgeHandler.publish(SLF4JBridgeHandler.java:303)
at java.util.logging.Logger.log(Unknown Source)
at java.util.logging.Logger.doLog(Unknown Source)
at java.util.logging.Logger.logp(Unknown Source)
at org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:181)
at org.apache.juli.logging.DirectJDKLog.error(DirectJDKLog.java:147)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1352)
at java.lang.Thread.run(Unknown Source)
I run the example on 64-bit HotSpot with Java8:
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)
Byte Buddy version is 1.4.32. Here is agent maven configuration:
<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>pl.halun.demo.bytebuddy</groupId>
<artifactId>byte-buddy-agent-demo</artifactId>
<version>1.0</version>
<properties>
<jdk.version>1.8</jdk.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.4.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>${project.artifactId}-${project.version}-full</finalName>
<appendAssemblyId>false</appendAssemblyId>
<archive>
<manifestEntries>
<Premain-Class>pl.halun.demo.bytebuddy.logging.LoggingAgent</Premain-Class>
<Agent-Class>pl.halun.demo.bytebuddy.logging.LoggingAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
<Can-Set-Native-Method-Prefix>true</Can-Set-Native-Method-Prefix>
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<id>assemble-all</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
And here is pom file for the instrumented application:
<?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>pl.halun.demo.bytebuddy.instrumented.app</groupId>
<artifactId>byte-buddy-agent-demo-instrumented-app</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
From my point of view it is very valuable option to add logs on the running server and I hate to loose this part of demo. I tried to experiment with different redefinition strategies, but until now nothing seems to work.
What you observe is a classical version conflict, I think. Spring Boot most likely comes with a version of ThrowableProxy that is not compatible to the version that is added with the Java agent. When loading a Java agent at runtime, Spring's version is already loaded whereas the startup attachment prepends the agent-bundled version on the class path where the version of your agent is loaded.
Java agents are typically added to the class path. This is also where your Spring boot application is living. You need to make sure that a Java agent does not contain dependencies that are incompatible with your application's dependencies or you need to shade all dependencies to avoid such conflicts.
There is however another problem: When writing a Java agent that is attached at runtime, you meet additional constraints on most JVMs where on HotSpot, you are not allowed to change the class file format of any class that is already loaded. There is also a chance that your class is already loaded where currently, no effect would be visible as you do not enable retransformation.
A runtime-capable agent would need to use the Advice component which inlines code into target code rather then using the classical delegation model:
class MyAdvice {
#Advice.OnMethodEnter
static void intercept(#Advice.BoxedArguments Object[] allArguments,
#Advice.Origin Method method) {
Logger logger = LoggerFactory.getLogger(method.getDeclaringClass());
logger.info("Method {} of class {} called", method.getName(), method
.getDeclaringClass().getSimpleName());
for (Object argument : allArguments) {
logger.info("Method {}, parameter type {}, value={}",
method.getName(), argument.getClass().getSimpleName(),
argument.toString());
}
}
}
You can use the above advice class by registering it as a visitor. Such visitors only apply to declared methods, i.e. not to inherited methods and inline their code into existing methods. This way, the logging will not be visible on the call stack and it also becomes legal to retransform already loaded classes:
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.type(isAnnotatedWith(annotationType))
.transform(new AgentBuilder.Transformer() {
#Override
public DynamicType.Builder<?> transform(
DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassLoader classLoader) {
return builder.visit(Advice.to(MyAdvice.class).on(named(methodName)));
}
});
As for the attachment, look into the byte-buddy-agent project which allows you to call:
ByteBuddyAgent.attach(agentJar, processId);
The above helper supports other VMs where the attachment API often lives in a different namespace.
Update: Here is the problem with Spring Boot. Spring Boot creates custom class loaders that have the system class loader (class path) as their parent. These class loaders consider classes from the system class loader first. When you add the agent, the entire Spring boot app is both on the class loader and in these child class loaders. A class like IThrowableProxy now exists twice in two class loaders but is not considered to be equal by the JVM. Depending on the state of the VM, some classes might already be linked to the original IThrowableProxy whereas other classes are loaded after the agent was attached and get linked to the new IThrowableProxy from the agent. Both classes are not equal and the error that you see is thrown where the VM complains that the class does not implement the correct IThrowableProxy (but the previous one). If the agent is attached at start up, this problem does not exist as the class path's IThrowableProxy is always loaded.
This is not an easy error to fix, in the end, Byte Buddy cannot help you with such class path issues and Spring Boot is quite free in its interpretation of the class loader contract. The easiest way would be to not use Spring Boot types in your agent. You can still match the annotation with for example
isAnnotatedWith(named("org.springframework.web.bind.annotation.RestController"))
The question is how you can communicate with Spring Boot. One work-arround would be to add all shared classes to the class path on start up. Typically, I do avoid the usage of shared classes altogether but only use them in the Advice classes where the code is inlined in the class loader of the target application. Simply set the Spring Boot dependency in provided scope, the advice code itself is never executed.
I'm trying to write a plugin to perform some custom Java checks.
The checks are pretty simple and all look similar to this:
#ActivatedByDefault
#SqaleConstantRemediation("1h")
#SqaleSubCharacteristic(SECURITY_FEATURES)
#Rule(name = "Methods annotated with #RequestMapping should also have #PreAuthorize annotation", priority = CRITICAL)
public class RequestMappingHasPreAuthorizeAnnotationCheck extends MethodVisitor {
#Override
public void visitMethod(MethodTree methodTree) {
if (hasRequestMappingButNotPreAuthorize(methodTree.modifiers())) {
reportIssue(methodTree,
"Methods annotated with #RequestMapping should also be annotated with #PreAuthorize");
}
}
private static boolean hasRequestMappingButNotPreAuthorize(ModifiersTree modifiers) {
return isAnnotatedWith(modifiers, REQUEST_MAPPING) && !isAnnotatedWith(modifiers, PRE_AUTHORIZE);
}
Relevant part of pom.xml:
<packaging>sonar-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.sonarsource.java</groupId>
<artifactId>java-checks</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.sonarsource.sonarqube</groupId>
<artifactId>sonar-plugin-api</artifactId>
<version>5.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
<artifactId>sonar-packaging-maven-plugin</artifactId>
<version>1.15</version>
<extensions>true</extensions>
<configuration>
<pluginClass>com.company.sonar.security.RestSecurityPlugin</pluginClass>
<pluginDescription>Verifies security annotations</pluginDescription>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-dev-maven-plugin</artifactId>
<version>1.8</version>
</plugin>
</plugins>
</build>
When I build with
mvn clean package
it generates my snapshot jar in target, but displays this (along w/ similar messages for other libraries):
[WARNING] com.google.guava:guava:jar:10.0.1:compile is provided by SonarQube plugin API and will not be packaged in your plugin
When I try to deploy it to my local test sonar server using
mvn sonar-dev:upload -DsonarHome=~/sonarqube-5.3
I get the folowing (snipped) stack trace:
Java::JavaLang::NoClassDefFoundError (com/google/common/collect/Iterables):
org.sonar.squidbridge.annotations.AnnotationBasedRulesDefinition.addRuleClasses(AnnotationBasedRulesDefinition.java:90)
org.sonar.squidbridge.annotations.AnnotationBasedRulesDefinition.addRuleClasses(AnnotationBasedRulesDefinition.java:86)
org.sonar.squidbridge.annotations.AnnotationBasedRulesDefinition.load(AnnotationBasedRulesDefinition.java:75)
com.company.sonar.security.RestSecurityRulesDefinition.define(RestSecurityRulesDefinition.java:25)
org.sonar.server.rule.RuleDefinitionsLoader.load(RuleDefinitionsLoader.java:54)
org.sonar.server.rule.RegisterRules.start(RegisterRules.java:100)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:497)
org.picocontainer.lifecycle.ReflectionLifecycleStrategy.invokeMethod(ReflectionLifecycleStrategy.java:110)
org.picocontainer.lifecycle.ReflectionLifecycleStrategy.start(ReflectionLifecycleStrategy.java:89)
org.sonar.core.platform.ComponentContainer$1.start(ComponentContainer.java:291)
org.picocontainer.injectors.AbstractInjectionFactory$LifecycleAdapter.start(AbstractInjectionFactory.java:84)
org.picocontainer.behaviors.AbstractBehavior.start(AbstractBehavior.java:169)
org.picocontainer.behaviors.Stored$RealComponentLifecycle.start(Stored.java:132)
org.picocontainer.behaviors.Stored.start(Stored.java:110)
org.picocontainer.DefaultPicoContainer.potentiallyStartAdapter(DefaultPicoContainer.java:1016)
org.picocontainer.DefaultPicoContainer.startAdapters(DefaultPicoContainer.java:1009)
org.picocontainer.DefaultPicoContainer.start(DefaultPicoContainer.java:767)
org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:131)
org.sonar.server.platform.platformlevel.PlatformLevel.start(PlatformLevel.java:84)
org.sonar.server.platform.platformlevel.PlatformLevelStartup.access$001(PlatformLevelStartup.java:45)
org.sonar.server.platform.platformlevel.PlatformLevelStartup$1.doPrivileged(PlatformLevelStartup.java:82)
org.sonar.server.user.DoPrivileged.execute(DoPrivileged.java:45)
org.sonar.server.platform.platformlevel.PlatformLevelStartup.start(PlatformLevelStartup.java:78)
org.sonar.server.platform.Platform.executeStartupTasks(Platform.java:197)
org.sonar.server.platform.Platform.restart(Platform.java:141)
org.sonar.server.platform.Platform.restart(Platform.java:125)
org.sonar.server.platform.ws.RestartAction.handle(RestartAction.java:63)
org.sonar.server.ws.WebServiceEngine.execute(WebServiceEngine.java:85)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
(snip)
The RestSecurityRulesDefinition class looks like this:
public class RestSecurityRulesDefinition implements RulesDefinition {
#Override
public void define(Context context) {
NewRepository repository = context.createRepository("rest-security", "java").setName("rest-security");
#SuppressWarnings("rawtypes")
List<Class> ruleClasses = new ArrayList<>();
ruleClasses.add(PreAuthorizeInfoCheck.class);
ruleClasses.add(RequestMappingHasPartnerSecuredAnnotationCheck.class);
ruleClasses.add(RequestMappingHasPreAuthorizeAnnotationCheck.class);
ruleClasses.add(RequestMappingHasTimedAnnotationCheck.class);
ruleClasses.add(RestControllerWithExceptionHandlerAnnotationCheck.class);
// exception gets thrown on the following line
AnnotationBasedRulesDefinition.load(repository, "java", ruleClasses);
repository.done();
}
Server is freshly downloaded 5.3 with sonar.web.dev=true.
Any help on what I might be missing?
I'm new to using Hibernate with Java. I'm getting the following exception. The stuff that I found online regarding this error didn't seem to help. Any ideas? The Exception:
java.lang.IllegalArgumentException: org.hibernate.hql.ast.QuerySyntaxException:
ApplPerfStats is not mapped [select count(c) from ApplPerfStats c]
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:601)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:96)
at com.icesoft.icefaces.samples.datatable.jpa.CustomerDAO.findTotalNumberCustomers(CustomerDAO.java:89)
at com.icesoft.icefaces.samples.datatable.ui.SessionBean.getDataPage(SessionBean.java:189)
at com.icesoft.icefaces.samples.datatable.ui.SessionBean.access$0(SessionBean.java:185)
at com.icesoft.icefaces.samples.datatable.ui.SessionBean$LocalDataModel.fetchPage(SessionBean.java:245)
at com.icesoft.icefaces.samples.datatable.ui.PagedListDataModel.getPage(PagedListDataModel.java:121)
at com.icesoft.icefaces.samples.datatable.ui.PagedListDataModel.getRowCount(PagedListDataModel.java:100)
at com.icesoft.faces.component.datapaginator.DataPaginator.isModelResultSet(DataPaginator.java:1091)
at com.icesoft.faces.component.datapaginator.DataPaginatorRenderer.encodeBegin(DataPaginatorRenderer.java:201)
The place where this is called:
#SuppressWarnings("unchecked")
public Long findTotalNumberCustomers() {
EntityManagerHelper.log("finding number of Customer instances", Level.INFO, null);
try {
String queryString = "select count(c) from ApplPerfStats c";
return (Long) getEntityManager().createQuery(queryString).getSingleResult();
} catch (RuntimeException re) {
EntityManagerHelper.log("find number of Appl_perf_stats failed",
Level.SEVERE, re);
throw re;
}
}
The class that maps to the database table:
package com.icesoft.icefaces.samples.datatable.jpa;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "Appl_perf_stats", uniqueConstraints = {})
public class ApplPerfStats implements java.io.Serializable {
.....
Thanks,
Tam
Try adding a class element under persistence-unit, in your persistence.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<persistence ...>
<persistence-unit name="unit">
<class>com.icesoft.icefaces.samples.datatable.jpa.ApplPerfStats</class>
...
</persistence-unit>
<persistence>
I haven't done much more than that with JPA/EntityManager, so I don't know if there's a way to add an entire package. AFAIK, when using hibernate.cfg.xml, each persistent class has to be specified directly.
It happened to me until I started to use the full class name, e.g.:
String queryString = "select count(c) from com.my.classes.package.ApplPerfStats c";
But I don't like this approach, because it's refactoring-unfirendly. A more tractable one would be:
String queryString = "select count(c) from " + ApplPerfStats.class.getName() + c";
javashlook's solution seems to be a shortcut for that - but it adds more XML configuration, which I try to avoid. If only there was an annotation-based way to specify that...
I was having the same problem and I solved by adding aspectj entries to my pom.xml see below. I guess it makes sense if you are using annotations. Otherwise you need to specify the mappings in the XML file. I had this problem from a project that was using a jar with JPA annotations.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.2</version> <!-- NB: do use 1.3 or 1.3.x due to MASPECTJ-90 - wait for 1.4 -->
<dependencies>
<!-- NB: You must use Maven 2.0.9 or above or these are ignored (see MNG-2972) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<configuration>
<weaveDependencies>
<weaveDependency> <groupId>your.project</groupId> <artifactId>your.artifact</artifactId> </weaveDependency>
</weaveDependencies>
</configuration>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
You should specify a column to do the count on
select count(c.someColumn) from ApplPerfStats c
Or try a count(*)
select count(*) from ApplPerfStats c
I faced this issue but in my case the problem was due to gradle version.
When I changed my system from linux to mac then I had to switch from gradle-1.0-milestone-3 to gradle-1.0-milestone-4 as milestone-3 does not work in OSX. And in gradle-1.0-milestone-4 I faced the same issue then I had to degrade my gradle version to gradle-1.0-milestone-1. Now it is working fine
I was also face this problem fixed by this ...
You haven't declared your entity classes in persistence.xml config file:
<property name="hibernate.archive.autodetection" value="class, hbm"/>
I know it has been a long time on this matter, but none of the above answers solved my problem.
I had already declared the entity in the persistence.xml file.
At the end the whole problem was simply that the name of the entity is case sensitive on my system.
This jpql was wrong
SELECT u FROM user u WHERE userId = 1?
But this one works fine
SELECT u FROM User u WHERE userId = 1?