Opening bean declared in Configuration class - spring-boot

Recently I encountered strange problem with Spring, Kotlin and Spock. I have very simple project (spring-boot, spring-web). I have one Controller with few Beans injected to this Controller. Everything works just fine. Problem is in test. I am not able to mock any of those Beans. kotlin-spring/kotlin-allopen does not add open signature to beans defined in Configuration class. On the other hand if I change this declaration to #Component everything works fine.
Here is my build.gradle.kts plugin listing
plugins {
id("idea")
id("groovy")
id("maven-publish")
id("org.springframework.cloud.contract") version "2.2.5.RELEASE"
id("org.springframework.boot") version "2.4.1"
id("io.spring.dependency-management") version "1.0.10.RELEASE"
kotlin("jvm") version "1.4.21"
kotlin("plugin.spring") version "1.4.21"
kotlin("plugin.allopen") version "1.4.21"
}
This is error message:
Caused by: org.spockframework.mock.CannotCreateMockException: Cannot create mock for class *** because Java mocks cannot mock final classes. If the code under test is written in Groovy, use a Groovy mock.
I know that it says that I can use GroovyMock but I wanted to design my base class in test and I wanted to use #TestConfiguration class. So to mock those classes I wanted to use DetachedMockFactory.
Is there a way to configure Spock to be able to mock final classes from Kotlin? Or is there a way to tell kotlin-spring/kotlin-allopen to open classes defined as beans in Configuration class?
Edit:
My example project is here:
https://github.com/czyzniek/bank/tree/with-spock

I cannot help you fix the spock-mockable problem, maybe you want to ask the author for help. Recently I fixed a Maven issue in that project already, but now it seems like there is a ByteBuddy issue, trying to redefine an already loaded class which is impossible in the JVM. I am sure the maintainer can help you with that.
Meanwhile, like I suggested in a previous comment, switching to Sarek solves the problem, though. I created a corresponding pull request for you.
For now it uses the full Sarek agent, which is the default. If you add sarek-unfinal as a dependency instead of sarek and also make sure that you set the system property dev.sarek.agent.type=unfinal in your Gradle configuration for Spock tests, you can use the smaller unfinal agent without the rest of the Sarek functionality instead. As a Gradle noob I do not know how to configure that, though.
Update: There is no more need for the additional system property mentioned above. The latest Sarek snapshot auto-detects the unfinal agent (or any other of the 4 types of Sarek agents) when it is on the class path. I know you have decided to use spock-mockable, but I want to keep this answer up to date for reference. See this diff for what the pull request would look like now, especially this simple commit.
Please let me know if you have any issues using the snapshot version for now. If this is a commercial project and you need to build a release in which snapshot versions are forbidden at some point, I can publish an 1.0 or maybe a 0.8 or whatever on Maven Central. For now I just added the Maven Central snapshot repository (Sonatype OSS) to your build.

Thanks for help everybody! #kriegaex your solution works like a charm, thanks for that!
Regarding spock-mockable, I talked with author of this library and he figured out why spock-mockable could not work with my setup (GH issue). It was because of spock-spring extension. The root cause is that spock-spring extension was loaded before spock-mockable. When Spring/Spock wanted to create mocks from #TestConfiguration class it couldn't, because kotlin-spring plugin does not open classes declared as beans in a #Configuration class and spock-mockable was not loaded at this time. joke changed the way his extension is loaded, so right now both solutions spock-mockable and sarek work.
I believe that concludes my problem ;)

Related

make implementation dependency available in another submodule

I have submodule rest that defines couple of dependencies with implementation. And I have another module app that uses implementation project(":rest").
However the dependencies of the rest declared with implementation are not available for app. I know implementation does that by design, but how to make them available without using original compile configuration?
It seems I need to use plugin java-library and use api configuration for that dependency.

Why does sonar think annotating non-public methods with #Transactional is wrong?

We use aspect4J and compile time weaving. Its perfectly valid to annotate private methods with #Transactional, or at least that is what I thought. I am curious as to why sonar has the following rule:
Non-public methods should not be "#Transactional"
My guess is that sonar must be unaware that there are multiple ways to weave aspects and that the Spring default of using dynamic proxies is not the only way.
Its also interesting to note that this is of severity "critical".
Two questions:
1 - Is sonar wrong to assume that annotating a private method with #Transactional is wrong?
2 - If they are wrong how do I file a bug with the sonar team?
We have also faced with a "#Transactional" issue in interfaces that appears to be a false positive:
http://jira.sonarsource.com/browse/SONARJAVA-967
Upgrading java plugin to latest version (3.3) fixed the issue.
As about non-public methods of the class, there is specification ticket in "Active" state:
http://jira.sonarsource.com/browse/RSPEC-2230
Answering your second question, I guess, their jira (http://jira.sonarsource.com) is the main bug-tracking system of the project.

java.lang.NoClassDefFoundError Could not initialize class org.springframework.mock.web.MockServletContext

We are using Spring-test-4.0.6 jar in test scope in our project. Under same project we also have javaee-6.0 dependency in provided scope.
I am getting this error in test case
Could not initialize class org.springframework.mock.web.MockServletContext at com.sample.TestWebDOMConfiguratorMultiple.setUp(TestWebDOMConfiguratorMultiple.java:77)
Surprisingly replacing Javaee-6.0 dependency with servlet-api-3.0.1 resolves this issue.
Note: Spring-4.0.6 pom has compile time optional dependency on servlet-api-3.0.1.
Question is why is it working with servlet-3.0.1 and not with javaee 6.0 as we are trying
to replace servlet-api-3.0.1 with javaee-6.0.
Thanks in advance.
Without knowing the exact artifact that you are referring to as javaee-6.0 and without being able to see the full stack trace, it appears that your javaee-6.0 dependency contains Servlet API 3.0; whereas, spring-test-4.0.6.RELEASE explicitly requires Servlet API 3.0.1.
So that is likely the source of your problem.
Regards,
Sam

Why does bnd add a uses directive for a package used only within a method body?

I have a project with a single source file, listed here in its entirety:
package com.acme.el;
public class ExpressionUtils {
public static Object evaluate() {
new org.apache.commons.el.ExpressionEvaluatorImpl();
return null;
}
}
The functionality is irrelevant to the question. When I build the project as an OSGi bundle using Gradle, the manifest contains the following instruction:
Export-Package: com.acme.el;uses:="org.apache.commons.el";version="1.0"
What baffles me is that uses directive. As I've understood the directive, it is meant to define dependencies on other packages that need to be propagated to other bundles importing this exported package - if my class definitions or method signatures refer to classes in the org.apache.commons.el package, for instance. But in this class, the dependency on org.apache.commons.el is completely contained within the body of a method. It is not exposed in the API, and no other bundle importing com.acme.el could ever get a hold of the ExpressionEvaluatorImpl instance created in the method. So the dependency shouldn't need to be propagated, right?
Did I misunderstand the meaning of the uses directive, or is its use here unnecessary?
I made a minimal example GitHub repo for reproduction which you can clone and import as a Gradle project in Eclipse.
If, in your bnd file, you set -experiments: true you should get the proper uses: clause, only based on public API references.
The problem is that bnd from the beginning has used all the imports to calculate the uses constraints. This was by far the easiest and nobody, so far, has ever complained about it. However, I did create the code to scan the public API but never felt confident enough to remove it from the experimental phase ... The current model is creating too many uses constraints but that should in general be the safe way.
This code has not been tested enough nor do I feel confident that changing this calculation will not create problems in existing builds. So I am in a bit of a bind here.
Gradle 1.7 uses bnd 2.1.0, instead of bnd 1.50.0 which is used by earlier Gradle distributions. This problem does not occur when using Gradle 1.7, as demonstrated by this GitHub repo.

Maven & Axis2 plugin - different stub code generation

I'm using Maven 3.0.4 and Axis2 1.5.1 plugin. I've a problem with the generation of a stub class using AXIS2 plugin on Maven.
Depending on the JDK currently configured on the build environment, AXIS2 plugin generates a different stub class source code. I've tried the generation using JDK 1.6 and then JDK 1.7.
Is there any way to "force" the JDK (i.e. 1.6) used by the AXIS2 plugin inside Maven (without changing environment)? (I would like to have a code generation independent from the environment)
Any help will be much appreciated.
I assume you select your JDK like described here :
http://maven.apache.org/plugins/maven-compiler-plugin/examples/compile-using-different-jdk.html
Then, I would generate the stub using a different profile, allowing you to override some configuration, but keeping the benefits of your overall conf.
Unfortunately, all my attempts are unsuccessful.
I have to resign myself to what I was shown in another post (http://maven.40175.n5.nabble.com/Maven-amp-Axis2-plugin-different-stub-code-generation-tc5730726.html ): if I really require this type of functionality, I may have to do this work yourself. Otherwise ask Axis2 devs if someone
will volunteer to do it.

Resources