Can't inject a Kotlin service into a Java class - java

I have the following Kotlin service:
#Service
open class KotlinServiceImpl(private val myRepository: MyRepository) : MyService { ... }
The Java class that's trying to get it:
private final MyService MyService;
public MyController(MyService myService) {
this.myService = myService;
}
The error:
Parameter 0 of constructor in MyController required a bean of type 'MyService' that could not be found.
I'm using Gradle, and have the needed dependencies:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.72'
And the relevant Kotlin plugin is also applied:
plugins {
id 'org.jetbrains.kotlin.jvm'
}
When I convert the Java interface MyService to Kotlin, I get the following error:
java.lang.NoClassDefFoundError: com/.../MyService
Using only Java classes solves the problem.

The class wasn't available at runtime. I finally noticed that our build file defines its own run task:
run {
dependsOn pathingJar
doFirst {
classpath = files(
"$buildDir/classes/java/main",
"$buildDir/resources/main",
pathingJar.archivePath)
}
Map<String, String> map = loadExecutionProperties()
map.put("SPRING_PROFILES_ACTIVE", "...")
map.put("APP_NAME", "app-name")
environment(map)
}
So I need to add "$buildDir/classes/kotlin/main" to files in classpath.
I hope this will help future users.

Related

How to do a request scoped dependency injection with Guice? I am getting [No scope is bound to RequestScoped] run time error

I am trying to do a simple dependency injection with Guice for a request scoped object that should be different for every request but the same across the request itself.
Provider:
import com.google.inject.servlet.RequestScoped;
#RequestScoped
public class MyServiceProvider implements Provider<MyService> {
#Override
public ExperimentService get() {
return new MyService();
}
}
Module:
import com.google.inject.servlet.RequestScoped;
public class CommonModule extends AbstractModule {
#Override
protected void configure() {
bind(MyService.class).toProvider(MyServiceProvider.class).in(RequestScoped.class);
}
}
Usage:
public class SomeClass {
#Inject
private MyService myservice;
}
However I am getting a runtime error when the servlet start with the following error:
Caused by: com.google.inject.CreationException: Unable to create injector, see the following errors:
1) [Guice/ScopeNotFound]: No scope is bound to RequestScoped.
Used at:
1 : MyModule.configure(MyModule.java:50)
\_ installed by: SomeOtherModule -> MyModule
2 : MyServiceProvider.class(MyServiceProvider.java:11)
at MyModule.configure(MyModule.java:50)
\_ installed by: SomeOtherModule -> MyModule
Learn more:
https://github.com/google/guice/wiki/SCOPE_NOT_FOUND
1 error
What exactly am I doing wrong and what does this error even mean?

spring boot - integration test autowired interface no such bean found

I have a spring-boot app that now needs to support multiple Object stores and selectively use the desired store based on the environment. Essentially what i have done is create an interface that each store repository then implements.
I have simplified the code for the examples.
I have created 2 beans for each store type based on the spring profile determining the env:
#Profile("env1")
#Bean
public store1Sdk buildClientStore1() {
return new store1sdk();
}
#Profile("env2")
#Bean
public store2Sdk buildClientStore2() {
return new store2sdk();
}
in the service layer I have autowired the interface and then in the repositories i have used #Profile to specify which instance of the interface to use.
public interface ObjectStore {
String download(String fileObjectKey);
...
}
#Service
public class ObjectHandlerService {
#Autowired
private ObjectStore objectStore;
public String getObject(String fileObjectKey) {
return objectStore.download(fileObjectKey);
}
...
}
#Repository
#Profile("env1")
public class Store1Repository implements ObjectStore {
#Autowired
private Store1Sdk store1client;
public String download(String fileObjectKey) {
return store1client.getObject(storeName, fileObjectKey);
}
}
When I start the application with the configured "env" this actually runs as expected. however when running the test I get the "no qualifying bean of type ObjectStore. expected at least 1 bean which qualifies as autowire candidate."
#ExtendWith({ SpringExtension.class })
#SpringBootTest(classes = Application.class)
#ActiveProfiles("env1,test")
public class ComposerServiceTest {
#Autowired
private ObjectHandlerService service;
#Test
void download_success() {
String response = service.getObject("testKey");
...
}
}
As noted in the #ActiveProfile on the test class there are some other environments e.g. dev,test,prod. I have tried playing around with Component scan, having impl and interface in the same package, etc, to no success. I feel like I am missing something obvious with the test setup. But could be something with my overall application config? my main aim with the solution is to avoid having something a long the lines of
if (store1Sdk != null) {
store1Sdk.download(fileObjectKey);
}
if (store2Sdk != null) {
store2Sdk.download(fileObjectKey);
}
Try #ActiveProfiles({"env1", "test"}).
Activate multiple profiles using #ActiveProfiles and specify profiles as an array.
this probrom because Store1Repository use #Profile("env1"), when you use #test,this class not invoke. try delete #Profile("env1") of Store1Repository.
if you use #test, both of store1Sdk/store2Sdk don't instanse, try add default instanse.eg:
#Bean
public store2Sdk buildClientStoreDefault() {
return new store2sdk();
}

CloudException: No suitable cloud connector found

My project has dependencies on spring-boot-starter-cloud-connectors and spring-cloud-localconfig-connector. Here is my code:
#Configuration
class MyConfig {
#Bean
public CloudFactory cloudFactory() {
CloudFactory cf = new CloudFactory();
cf.registerCloudConnector(new LocalConfigConnector());
return cf;
}
}
#Component
class MyComponent {
#Autowired
CloudFactory cf;
#EventListener(value = ApplicationStartedEvent.class)
public void postConstruct() {
Cloud cloud = cf.getCloud();
}
}
When I try to run the above code locally, I get an exception saying:
org.springframework.cloud.CloudException: No suitable cloud connector found
Although, the parent's version is irrelevant but I am using 2.1.8.RELEASE.
Can someone point out what's wrong with the above code?
You have to add the default datasource:
mysql://user:password#localhost:3306/databasename
You can set it using the run configuration or create a seperate properties file for it.

Service Activator without method in Spring Integration Java Config / DSL

When using XML-based configuration for service-activator you can exclude the method as follows:
<service-activator input-channel="incomingCustomerChannel" output-channel="outgoingCustomerChannel" ref="customerService" />
This will cause the SI framework to choose the target method in customerService based on the payload. How can I achieve the same functionality using DSL and Java config?
At present I have the following:
#Bean
public IntegrationFlow customerRequestFlow(ConnectionFactory connectionFactory) {
return IntegrationFlows.from((MessagingGateways g) -> g.jms(connectionFactory)
.correlationKey("JmsCorrelationID")
.destination("customer_incoming.queue"))
.handle("customerService", "addCustomer")
.get();
}
The service activator is defined as:
#Component
public class customerService {
#ServiceActivator
public AddCustomerResponse addCustomer(AddCustomerRequest addCustomerRequest) {
// add customer
}
}
I have extended the activator to add a deleteCustomer method as follows:
#Component
public class customerService {
#ServiceActivator
public AddCustomerResponse addCustomer(AddCustomerRequest request) {
// add customer
}
#ServiceActivator
public DeleteCustomerResponse deleteCustomer(DeleteCustomerRequest request) {
// delete customer
}
}
I cannot simply remove the , "addCustomer" from .handle("customerService", "addCustomer") as methodName is mandatory. Is it possible to achieve this in Java config / DSL?
You can use the same .handle() using null for method name:
.handle("customerService", "addCustomer")
Or starting with version 1.1 you can use a new version of that method:
#Autowired
private CustomerService customerService;
....
.handle(this.customerService)
Both variants do exactly the same what you have with just ref for <service-activator>.

Cucumber Test a Spring Boot Application

Does anyone know where I can find a sample application where Cucumber is used to test a Spring Boot application through Gradle? I can run the tests fine starting the server on the cmd line and using my IDE, but I need to be able to run them all programmatically on the CI server. I saw the answer on here but that solution did not work for me, most likely because I have multiple step def files.
Here is my setup
build.grade (Mentioned in the other question)
testCompile ("org.springframework.boot:spring-boot-starter-test",
...
"info.cukes:cucumber-spring:${cucumberVersion}")
CucumberTest.java
#RunWith(Cucumber.class)
#CucumberOptions(format = "pretty", features = "src/test/resources")
public class CucumberTest{
}
AbstractSpringTest.java (Extended by all the StepDef files)
#SpringApplicationConfiguration(classes = Application.class)
#RunWith(SpringJUnit4ClassRunner.class)
#Ignore
public abstract class AbstractSpringTest
{ ... }
It's not doing the correct thing on start up because its 1. trying to initialize my step def files and 2. My application is not started and the cucumber tests cannot make a connection.
Thanks.
EDIT: Or if someone can tell me how to start and stop the application using gradle, that would be acceptable as well.
I have solved the issue with some help from this question.
Here is the repository with the answer:
https://github.com/jakehschwartz/spring-boot-cucumber-example
In short, the AbstractSpringTest class needs to have the following annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = DemoApplication.class, loader = SpringApplicationContextLoader.class)
#WebAppConfiguration
#IntegrationTest
I had a similar symptom, my cucumber wouldn't start up the Spring context...
Turns out I had missed (one of) the following dependencies:
build.gradle
testCompile "info.cukes:cucumber-junit:1.2.4"
testCompile "info.cukes:cucumber-java:1.2.4"
testCompile "info.cukes:cucumber-spring:1.2.4"
StepDefs.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(
loader = SpringApplicationContextLoader.class,
classes = Application.class
)
#WebIntegrationTest(randomPort = true)
public class StepDefs {
#Value("${local.server.port}")
int port;
}
Update: SpringBoot 1.5.1
#ContextConfiguration(
loader = SpringBootContextLoader.class,
classes = Application.class
)
Further to #jakehschwartz, if you want the web app to start on a random available port, AbstractSpringTest needs:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class, loader = SpringApplicationContextLoader.class)
#WebIntegrationTest({"server.port=0"})
public abstract class AbstractSpringTest {
#Value("${local.server.port}")
protected int serverPort;
...}
I did something like this to get Spring to work with JUnit parameterized tests. It should be the same concept for Cucumber, but I haven't tried it. I was using XML configuration, so that might make a difference.
RunWithSpringJUnit4
public abstract class RunWithSpringJUnit4 {
private TestContextManager testContextManager;
public RunWithSpringJUnit4() {
try {
this.testContextManager = new TestContextManager(getClass());
this.testContextManager.prepareTestInstance(this);
} catch (Exception e) {
e.printStackTrace();
}
}
}
CucumberTest
#RunWith(Cucumber.class)
#CucumberOptions(format = "pretty", features = "src/test/resources")
public class CucumberTest extends RunWithSpringJUnit4 {
}
First, you'll need to ensure that you have applied spring-boot in gradle. Invoke gradle build which will produce a runnable jar. Instead of having your manifest call for the Spring class as your main, have a wrapper that starts it in a thread, waits for it to settle down and runs Cucumber:
#RunWith(Cucumber.class)
public class LucasePsCucumberTest implements Runnable {
public static void main(String[] args) {
Thread t = new Thread(this);
t.start();
// wait for t
cucumber.api.cli.Main(null);
}
}

Categories