How to connect a grails app to Consul through micronaut Http Client? - java

I have a grails 3.2.0 web profile app. I want to connect to a separate micronaut app which is running fine and is connected properly to consul locally.
Here is my micronaut controller :
#Controller("/test")
class TestController {
#Get("/")
def index(){
return "Welcome to micronaut"
}
#Get("/test")
Single<String> test() {
return Single.just("Hello There")
}
}
Here is my grails controller :
class CardClientController {
#Autowired
NewCardClient newCardClient
def index() {
def result = newCardClient.getCards()
respond result
}
}
NewCardClient interface :
#Client("firstmicroservice")
public interface NewCardClient {
#Get("/test/test")
Single<String> getCards();
}
dependencies i've added in grails app :
compile 'io.micronaut:inject-java:1.0.0.M1'
compile 'io.micronaut:discovery-client:1.0.0.M1'
compile 'io.micronaut:spring:1.0.0.M1'
runtime 'io.micronaut:runtime:1.0.0.M1'
When i hit the grails controller as /cardClient/, i am getting following error:
2019-05-03 18:20:30.333 ERROR --- [nio-8200-exec-1] i.m.retry.intercept.RecoveryInterceptor : Type [grailsmono.NewCardClient$Intercepted] executed with error: Cannot convert publisher into target type: class io.reactivex.Flowable
java.lang.UnsupportedOperationException: Cannot convert publisher into target type: class io.reactivex.Flowable
at io.micronaut.cache.interceptor.CacheInterceptor.interceptPublisher(CacheInterceptor.java:352)
at io.micronaut.cache.interceptor.CacheInterceptor.intercept(CacheInterceptor.java:120)
at io.micronaut.aop.MethodInterceptor.intercept(MethodInterceptor.java:41)
at io.micronaut.aop.chain.InterceptorChain.proceed(InterceptorChain.java:155)
at io.micronaut.discovery.client.$CachingCompositeDiscoveryClientDefinition$Intercepted.getInstances(Unknown Source)
at io.micronaut.discovery.client.CachingCompositeDiscoveryClient.getInstances(CachingCompositeDiscoveryClient.java:38)
at io.micronaut.http.client.loadbalance.DiscoveryClientRoundRobinLoadBalancer.select(DiscoveryClientRoundRobinLoadBalancer.java:58)
at io.micronaut.http.client.DefaultHttpClient.resolveRequestURI(DefaultHttpClient.java:720)
at io.micronaut.http.client.DefaultHttpClient.exchange(DefaultHttpClient.java:410)
at io.micronaut.http.client.DefaultHttpClient.exchange(DefaultHttpClient.java:154)
at io.micronaut.http.client.HttpClient.retrieve(HttpClient.java:125)
at io.micronaut.http.client.RxHttpClient.retrieve(RxHttpClient.java:60)
at io.micronaut.http.client.RxHttpClient.retrieve(RxHttpClient.java:33)
at io.micronaut.http.client.interceptor.HttpClientIntroductionAdvice.intercept(HttpClientIntroductionAdvice.java:331)
at io.micronaut.aop.MethodInterceptor.intercept(MethodInterceptor.java:41)
at io.micronaut.aop.chain.InterceptorChain.proceed(InterceptorChain.java:155)
at io.micronaut.retry.intercept.RecoveryInterceptor.intercept(RecoveryInterceptor.java:74)
at io.micronaut.aop.MethodInterceptor.intercept(MethodInterceptor.java:41)
at io.micronaut.aop.chain.InterceptorChain.proceed(InterceptorChain.java:155)
at grailsmono.NewCardClient$Intercepted.getCards(Unknown Source)
...
Couldn't connect to consul and not able to debug the cause. Please help!!!

With the help of #JeffScottBrown, my workaround got simple just by changing the dependencies version of each as follows :
Added mavenCentral() repository as dependency :
repositories {
mavenLocal()
mavenCentral()
maven { url "https://repo.grails.org/grails/core" }
}
and the required micronaut dependencies as follows :
compile 'io.micronaut:micronaut-inject-java:1.1.0'
compile 'io.micronaut:micronaut-discovery-client:1.1.0'
compile 'io.micronaut:micronaut-spring:1.1.0'
runtime 'io.micronaut:micronaut-runtime:1.1.0'

Related

Java EE 8 application failed to start on Wildfly 25 after adding Extension

I am faced with a strange issue trying to create a custom Scope in my application.
I made minimal sample application to illustrate the problem.
Environment:
Java 11 (but same with 17 either)
Wildfly 25.0.0.Final
build.gradle:
plugins {
id 'java'
id 'war'
}
group 'com.example.extension'
version '1.0-SNAPSHOT'
sourceCompatibility = 11
war {
archiveName 'extension.war'
}
repositories {
mavenCentral()
}
dependencies {
compileOnly group: 'jakarta.platform', name: 'jakarta.jakartaee-api', version: '8.0.0'
}
Main.java
package com.example.extension;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
#Singleton
#Startup
public class Main {
#Inject
SampleClass sc;
#PostConstruct
public void init() {
sc.sayHello();
}
}
SampleClass.java
package com.example.extension;
import javax.enterprise.context.ApplicationScoped;
#ApplicationScoped
public class SampleClass {
public void sayHello() {
System.out.println("Hello world");
}
}
At that stage everything is ok:
INFO [stdout] (ServerService Thread Pool -- 78) Hello world
But after adding a new empty class to the project
SampleExtension.java
package com.example.extension;
import javax.enterprise.inject.spi.Extension;
public class SampleExtension implements Extension { }
It starts to fail:
com.example.extension : extension.war: java.lang.Exception: {"WFLYCTL0080: Failed services" => {"jboss.deployment.unit."extension.war".component.Main.WeldInstantiator" => "Failed to start service
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type SampleClass with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject com.example.extension.Main.sc
at com.example.extension.Main.sc(Main.java:0)
"}}
com.example.extension : extension.war: Artifact is deployed successfully
I'm new in the Java EE world and I stuck with it. Please explain what is wrong with my code?
P.S. I've checked the same code using Jakarta EE 9.1 and GlassFish 6.2.2 (have to change imports from javax to jakarta) and there is no such issue.
Given you're adding a CDI extension you need to include a beans.xml in your deployment. Per the specification:
An archive which:
contains a beans.xml file with the bean-discovery-mode of none, or,
contains an extension and no beans.xml file
is not a bean archive.

UnknownHostException for embedded Mongo DB tests in Jenkins build

I have a Spring Boot REST service and some unit tests written for the data layer. I use the embedded MongoDB dependency to perform the basic CRUD tests for my Repository class:
public interface UserRepository extends MongoRepository<UserEntity, String> {
Optional<UserEntity> findByUsername(String username);
}
I load the data from a JSON file (located under test/java/resources/data) and with the help of an ObjectMapper instance, I load the data into the embedded DB before each test and drop the collection after it's completed:
#DataMongoTest
class UserRepositoryTest {
// the path to the JSON file
private final File USER_DATA_JSON = Paths.get("src", "test", "resources", "data", "UserData.json").toFile();
// used to load a JSON file into a list of Users
private final ObjectMapper objectMapper = new ObjectMapper();
#Autowired
private MongoTemplate mongoTemplate; // makes the interaction with the embedded MongoDB much easier
#Autowired
private UserRepository userRepository;
#BeforeEach
void setUp() throws IOException {
// deserialize the JSON file to an array of users
UserEntity[] users = objectMapper.readValue(USER_DATA_JSON, UserEntity[].class);
// load each user into embedded MongoDB
Arrays.stream(users).forEach(mongoTemplate::save);
}
#AfterEach
void tearDown() {
// drop the users collection
mongoTemplate.dropCollection("users");
}
#Test
void testFindAllSuccess() {
// WHEN
List<UserEntity> users = userRepository.findAll();
// THEN
assertEquals(2, users.size(), "findAll() should return 2 users!");
}
// other test methods
}
In my local environment, everything works just fine, no configuration is needed inside the application.properties file. But when I execute a build on Jenkins, the following error appears for all the Repository tests:
UserRepositoryTest > testFindAllSuccess() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:800
Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:658
Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:185
Caused by: java.net.UnknownHostException at InetAddress.java:1642
Caused by: java.net.UnknownHostException at Inet6AddressImpl.java:-2
The build.gradle file dependencies are declared as follows:
implementation 'org.openapitools:openapi-generator-gradle-plugin:5.0.0'
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation "io.springfox:springfox-boot-starter:3.0.0"
implementation('org.modelmapper:modelmapper:2.3.0')
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'de.flapdoodle.embed:de.flapdoodle.embed.mongo'
testImplementation 'io.projectreactor:reactor-test'
compile 'io.springfox:springfox-swagger-ui:3.0.0'
annotationProcessor group: 'org.springframework.boot', name: 'spring-boot-configuration-processor'
compile 'org.mongodb:mongodb-driver-sync'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-test'
// https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt
implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1'
I assume Spring Boot is not identifying the embedded MongoDB anymore. How can I configure it as simple as possible?
I saw there is a possibility of using containerized MongoDB and test it using #TestContainers, but for now I need to fix this issue since every build fails due to the tests.
LATER EDIT: By activating the --debug option in Jenkins when running the build, I discovered the following cause:
java.net.UnknownHostException: dev096.dev.cloud.******.eu: dev096.dev.cloud.******.eu: Name or service not known
Do you know if I have to add that address to the known hosts to my local machine (etc/hosts) or it should be configured locally with profiles (localhost for development and dev096.dev.cloud.******.eu for production)?
I managed to reproduce the same error locally and, if you haven't solved it yet, this is my take.
Remove all MongoDB custom configurations/properties, then add the following class to your project:
#Configuration
public class MongoTestConfig {
#Bean
MongoProperties properties() {
MongoProperties mongoProperties = new MongoProperties();
mongoProperties.setPort(33333);
mongoProperties.setHost("localhost");
return mongoProperties;
}
}
Make sure you tag your test class (UserRepositoryTest) with these three annotations:
#RunWith(SpringRunner.class)
#DataMongoTest
#Import(MongoTestConfig.class)
#DataMongoTest ignores all other bean definitions so we force the test to include the configuration that we just created using the explicit #Import.
Now the test will be forced to run on localhost. Hope it's going to work for you as it did for me! 😁
If dev096.dev.cloud.******.eu is a host used only in production, then your production server needs to know that host; not your local PC, nor Jenkins.
Ideally, you'd run your Jenkins tests using a 'jenkins' Spring profile, and then define the localhost in application-jenkins.properties.
Unfortunately, none of your solutions worked for me, but I found out later that dev096.dev.cloud.******.eu is the Jenkins instance itself.
The fix was to add an entry in /etc/hosts/ such that the server's IP to be also recognized as dev096.dev.cloud.******.eu

Unable to return Freemarker view from a Spring Boot method

Currently, I am doing a small project in which I need to use Spring Boot and FreeMarker template engine. I have tried different ways but I still cannot return a FreeMarker view from Spring Boot. My project uses Gradle as the build tool, here's what's inside:
build.gradle:
plugins {
id 'org.springframework.boot' version '2.3.3.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
sourceCompatibility = 11
repositories {
mavenCentral()
}
sourceSets.main.resources.srcDirs = ["src/resources"]
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
compile("org.springframework.boot:spring-boot-starter-web")
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'org.springframework.boot:spring-boot-starter-freemarker'
}
Here is my method which I expect to redirect to the FreeMarker view with a list, instead, when I send a GET request to this URL, I just get the "index" string displayed:
#GetMapping(path = "/code/latest")
public String getTop10LatestCode(#ModelAttribute("model") ModelMap model) {
model.addAttribute("codes", codes.subList(Math.max(0, codes.size() - 10), Math.max(codes.size() - 1, 0)));
return "index";
}
Here is my application.properties file:
server.port=8889
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true
spring.freemarker.template-loader-path=classpath:/templates/
spring.freemarker.suffix=.ftlh
My FreeMarker view is already under the templates folder inside the resources folder.
Here is my project structure:
Any help would be much appreciated, thanks.
I have figured it out, my class is annotated with the #RestController annotation, which itself is the combination of the #Controller and #ResponseBody annotations, because each method in my class will have a response body returned, even the one that I posted here, that's why it returns the body, hence returns index. I need to change my #RestController to #Controller and the problem solved.

OptaPlanner SolverManager Bean not being found in Spring application

I am fairly new to working with Spring and I've come across a problem I cannot seem to resolve. I am trying to work with a package called Optaplanner. In this case I'm more or less following along with a simple course scheduler the video is here and the part I am working on happens at 51:00.
My problem is, one step of the process requires a dependency injection and when I build/run the Spring application I get the following error:
Description:
Field solverManager in com.java.optaplex.resource.MainResource required a bean of type 'org.optaplanner.core.api.solver.SolverManager' that could not be found.
The injection point has the following annotations:
- #javax.inject.Inject()
Action:
Consider defining a bean of type 'org.optaplanner.core.api.solver.SolverManager' in your configuration.
Process finished with exit code 1
I have used Gradle to manage my optaplanner package and I can see that the dependency is present (it is an interface called SolverManager). I cannot figure out why it cannot find/generate the bean however. I am assuming Spring would generate the bean on the fly much like a JPA repository's bean is created when using the #Autowired decorator. I have tried copying and pasting the SolverManager code to its own file under my project root and tried a few decorators on it (eg. #Component) in hopes that Spring would detect it, yet it still throws the same error and I am unsure what to do.
I have created a very simple demo app on GitHub here that has the error. The error is in the file src/main/java/com/java/optaplex/resource/MainResource.java
Also, in IntelliJ the SolverManager instance solverManager (see code below) is highlighted and says:
Could not autowire. No beans of 'SolverManager<Test, Long>' type found.
MainResource.java:
package com.java.optaplex.resource;
import com.java.optaplex.pojo.Test;
import org.optaplanner.core.api.solver.SolverManager;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.inject.Inject;
#RestController
#RequestMapping("/api")
public class MainResource {
// THE OFFENDING LINE IS HERE
#Inject
SolverManager<Test, Long> solverManager;
#GetMapping("/test")
public Test test () {
return new Test("Result message here");
}
}
For clarity, my Test class in the declaration of the Solver manager is:
Test.java
package com.java.optaplex.pojo;
public class Test {
private String message;
public Test(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
I realize my example code doesn't actually make use of the SolverManager, I just want the error on build to stop so I can proceed developing this. I just wanted to provide a very simple case that caused the error.
Lastly, my Gradle dependencies look like this:
build.gradle:
plugins {
id 'org.springframework.boot' version '2.2.6.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
id 'war'
}
group = 'com.java'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
// https://mvnrepository.com/artifact/org.optaplanner/optaplanner-core
compile group: 'org.optaplanner', name: 'optaplanner-core', version: '7.36.1.Final'
compile group: 'javax.inject', name: 'javax.inject', version: '1'
}
test {
useJUnitPlatform()
}
To make it work you just need to replace the #Inject annotation with #Autowire.
From the latest versions of Spring, the best practice is to use constructor dependency injection instead of #Autowire (the annotation is not required anymore as Spring will autowire beans automatically.
Try this:
#RestController
#RequestMapping("/api")
public class MainResource {
// Just the definition here
private SolverManager<Test, Long> solverManager;
pubilc MainResource(SolverManager<Test, Long> solverManager) {
// When Spring will start, the bean will be injected here and become
// available in this class
this.solverManager = solverManager;
}
#GetMapping("/test")
public Test test () {
// .....
}
// Other controller methods ....
}
I'm using IntelliJ, and it reports the error to me too. But I'm using the same code that I've posted in this answer and it works.
As you stated, the SolverManager bean is configured inside the optaplanner artifact, I suspect that the problem here is only the wrong annotation being used.
The IDE error bothers me too! But for the moment it works and I will leave it like this. In case I'll find a solution then I will add it here.
I am developing a API REST of route optimization with Spring Boot using OptaPlanner. I am just getting into the work with OptaPlanner and at one point I had this same problem. I had 'optaplanner-spring-boot-starter' and I added spring-boot-configuration-processor', with the idea of having the bean creation configuration that allows injecting the SolverManager in my classes, without success. Also I tried to change to other versions of OptaPlanner and without success. Finally I solved the problem adding the configuration that OptaPlanner includes in its code with the class 'RouteOptimizerConfig'. This class creates the bean of the solver, in such a way that already later I could inject it. This problem seems to be related to the fact that at a certain point the OptaPlanner SolverManager is overwritten.
Try this and add this class to your project:
#Configuration
class SolverConfig {
private final SolverFactory<VehicleRoutingSolution> solverFactory;
SolverConfig (SolverFactory<VehicleRoutingSolution> solverFactory) {
this.solverFactory = solverFactory;
}
#Bean
Solver<VehicleRoutingSolution> solver() {
return solverFactory.buildSolver();
}
}
It turns out, after a lot of online searches and digging through the Optaplanner Spring Boot packages the SolverManager bean is not provided for Spring. It seems the development focuses on the Quarkus framework.
I figured it was a good chance to try out Quarkus and once I made the change my code worked. There are some good videos on Youtube by the lead developer Geoffrey De Smet on setting up Optaplanner via Quarkus.

Cannot instantiate #InjectMocks field

I have created a wrapper for logging over log4j extending HandlerInterceptorAdapter keeping build type maven project. I am using this project as an external JAR in my Spring Boot application. Using Logger in my controllers. I have written JUnits for it which are working fine but got exception during mvn install.
DemoLogger is an interface exposing functions to log. DemoLogger is part of maven project which is an external jar.
Controller
#RestController #RequestMapping("/api/v1")
public class DemoController {
private DemoLogger logger = LoggerFactory.createLog(DemoController.class);
#GetMapping("/demo")
public ResponseEntity<String> getString() {
logger.info("This is test debug");
return new ResponseEntity("Hello World", HttpStatus.OK);
}
}
JUnit
public class DemoControllerTest {
#InjectMocks private DemoController demoController;
#Before public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test public void shouldReturnString()throws Exception {
final String body = demoController.getString().getBody().toString();
assertEquals("Hello World", body);
}
}
Error Log
Cannot instantiate #InjectMocks field named 'demoController' of type 'class dev.example.controller.DemoController'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : dev/example2/logger/factory/LoggerFactory
at dev.example.controller.DemoControllerTest.setup(DemoControllerTest.java:18)
Caused by: java.lang.NoClassDefFoundError: dev/example2/logger/factory/LoggerFactory
This seems more like a Maven problem that Mockito.
Most likely you are using that jar without specifying it in your pom as a dependency.
It works in your local IDE as most likely you added it manually to the classpath.
Try to install that jar in your local .m2 (or ideally on your company Nexus or something similar) and then run the build:
mvn install:install-file
More info here.

Categories