wanted to ask a basic question about webflux and resilience4j's retry.
We are running Java SpringBoot with Webflux and Resilience4J (not Spring Cloud Circuit Breaker).
While running the application its giving error (Somehow full stack trace is not coming for me) :
due to exception [reactor.core.publisher.Mono.retryWhen(Ljava/util/function/Function;)Lreactor/core/publisher/Mono;]
Code to build the Retry bean is as follows (we are not using YAML based configuration):
#Bean
public Retry retryConfig(ResilienceCCMConfig resilienceCCMConfig) {
RetryConfig config = RetryConfig.custom().maxAttempts(5)
.waitDuration(Duration.of(1, SECONDS))
.retryExceptions(ServerException.class)
.ignoreExceptions(ClientException.class)
.build();
return RetryRegistry.of(config).retry("retry-config", config);
}
Now we are calling our web client's POST method as below with bean injected from above :
#Autowired
private Retry retryConfig;
return webClient.post().uri(url)
.headers(httpHeaders -> getItemHeaders(httpHeaders, tenantId, bannerId, correlationId))
.body(itemServiceRequestMono, ItemServiceRequest.class)
.retrieve()
.onStatus(HttpStatus::isError, clientResponse -> {
LOGGER.error("Error");
if (clientResponse.statusCode().is5xxClientError()) {
throw new Exception("Something went wrong ");
}
return Mono.empty();
})
.bodyToMono(MyResponse.class)
.transform(RetryOperator.of(retryConfig))
.onErrorResume(ex -> myFallbackMethod(ex));
POM dependency :
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-reactor</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId> io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.6.1</version>
</dependency>
Please help .
Related
I am trying to create a keycloak object to register new user with keycloak with the following code:
public Keycloak getInstance() {
if (keycloak == null) {
return KeycloakBuilder.builder()
.realm(realm)
.serverUrl(serverURL)
.clientId(clientID)
.clientSecret(clientSecret)
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.build();
}
return keycloak;
}
But i keep getting the following error:
java.lang.ClassCastException: class org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl cannot be cast to class javax.ws.rs.client.ClientBuilder (org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl and javax.ws.rs.client.ClientBuilder are in unnamed module of loader 'app')
at org.keycloak.admin.client.ClientBuilderWrapper.create(ClientBuilderWrapper.java:29) ~[keycloak-admin-client-17.0.1.jar:17.0.1]
at org.keycloak.admin.client.spi.ResteasyClientClassicProvider.newRestEasyClient(ResteasyClientClassicProvider.java:35) ~[keycloak-admin-client-17.0.1.jar:17.0.1]
at org.keycloak.admin.client.Keycloak.newRestEasyClient(Keycloak.java:98) ~[keycloak-admin-client-17.0.1.jar:17.0.1]
at org.keycloak.admin.client.Keycloak.<init>(Keycloak.java:89) ~[keycloak-admin-client-17.0.1.jar:17.0.1]
at org.keycloak.admin.client.KeycloakBuilder.build(KeycloakBuilder.java:146) ~[keycloak-admin-client-17.0.1.jar:17.0.1]
at com.microfinanceBank.Customer.Config.KeycloakProvider.getInstance(KeycloakProvider.java:41) ~[classes/:na]
at com.microfinanceBank.Customer.service.KeycloakAdminClientServices.wow(KeycloakAdminClientServices.java:31) ~[classes/:na]
at com.microfinanceBank.Customer.controller.CustomerController.getCustomer(CustomerController.java:68) ~[classes/:na]
at com.microfinanceBank.Customer.controller.CustomerController$$FastClassBySpringCGLIB$$8c9f9beb.invoke(<generated>) ~[classes/:na]
Below are my keycloak dependencies:
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>17.0.1</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>6.1.0.Alpha1</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>6.1.0.Alpha1</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>6.1.0.Alpha1</version>
</dependency>
<dependency>
<groupId>com.guicedee.services</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>1.2.2.1</version>
</dependency>
What am i doing wrong?
I have tried adding some dependencies yet keep getting error.
Please what should i do.Thanks in advance
You can use keycloak's SDK for your springboot application. Include these two dependencies in the pom.xml
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>18.0.2</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>18.0.2</version>
</dependency>
After that create a keycloak's instance in your springboot application.
public Keycloak getAdminKeycloakUser() {
return KeycloakBuilder.builder().serverUrl("keycloak-auth-url")
.grantType("password").realm("realm-name")
.clientId("client-id")
.username("admin-user")
.password("admin-password")
.resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build()).build();
}
The above method will get you the admin user which is needed in order to create users into Keycloak.
The next step would be to get the realm as realm manages users.
public RealmResource getRealm() {
return getAdminKeycloakUser().realm(realm);
}
Finally now you can create user by using org.keycloak.representations.idm.UserRepresentation.
public void createUser() {
UserRepresentation userRepresentation = new UserRepresentation();
userRepresentation.setUsername("username");
userRepresentation.setFirstName("first-name");
userRepresentation.setLastName("last-name");
userRepresentation.setEmail("test#email.com");
Response response = getRealm().realmResource.users().create(userRepresentation);
//If user is created successfully 200 is returned for response status.
//Set password flow
CredentialRepresentation passwordCred = new CredentialRepresentation();
String userId = CreatedResponseUtil.getCreatedId(response);
passwordCred.setTemporary(false);
passwordCred.setType("password");
passwordCred.setValue("some-password");
UserResource userResource = realmResource.users().get(userId);
userResource.resetPassword(passwordCred);
}
keycloak-spring-boot-starter is deprecated, don't use it. You might find alternatives here
I wouldn't use keycloak-admin-client neither. Admin API is quite well documented. Just use your favorite REST client (with spring-boot features to configure OAuth2 REST client if you like) and POST a request to your Keycloak server.
UserRepresentation userRepresentation = new UserRepresentation();
userRepresentation.setUsername("username");
userRepresentation.setFirstName("first-name");
userRepresentation.setLastName("last-name");
userRepresentation.setEmail("test#email.com");
// set other required values
javax.ws.rs.core.Response response = KeycloakBuilder.builder().build().realm("your-realm-nam").users().create(userRepresentation);
if (response != null && response.getStatusInfo().getFamily() == Family.SUCCESSFUL)
{
return org.keycloak.admin.client.CreatedResponseUtil.getCreatedId(response); // returns String (Id of created User)
}
I'm facing issue with Swagger Integration in Spring Boot. Have a look at the code and error snippet.
------------------POM--------------------
<properties>
<java.version>1.8</java.version>
<swagger.version>2.9.2</swagger.version>
</properties>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>${swagger.version}</version>
</dependency>
-----------------App class--------------
#SpringBootApplication
#EnableSwagger2
public class ProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ServletPocProducerApplication.class, args);
}
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
Stack Trace
org.springframework.context.ApplicationContextException: Failed to start bean
'documentationPluginsBootstrapper'; nested exception is
java.lang.NullPointerException: Cannot invoke
"org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.toString()"
because the return value of
"springfox.documentation.spi.service.contexts.Orderings.patternsCondition(springfox.docume
ntation.RequestHandler)" is null
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.13.jar:5.3.13]
How do I fix this??
I solved it by adding "spring.mvc.pathmatch.matching-strategy=ant-path-matcher" in application.properties.
For a long time I have tried to solve this problem and solution for this is:
a) adding this to application.properties:
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
b) adding this to application.yaml(or application.yml):
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
I know this does not solve your problem directly, but consider moving to springdoc. Springfox is so buggy at this point that is a pain to use. I've moved to springdoc 2 years ago because of its Spring WebFlux support and I am very happy about it. Additionally, it also supports Kotlin Coroutines, which I am not sure Springfox does.
If you decide to migrate, springdoc even has a migration guide.
For the integration between spring-boot and swagger-ui, add the library to the list of your project dependencies (No additional configuration is needed):
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.12</version>
</dependency>
The Swagger UI page will then be available at
http://server:port/context-path/swagger-ui.html and the OpenAPI
description will be available at the following url for json format:
http://server:port/context-path/v3/api-docs
server: The server name or IP
port: The server port
context-path: The context path of the application
Adding this "spring.mvc.pathmatch.matching-strategy=ant-path-matcher" to your application.properties file solves the problem.
It's what i used and i saved me alot of trouble.
My suggestion is when you are using spring-boot then it is better to use spring boot dependency for swagger. So, spring-boot will take care of your default settings.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>...</version>
</dependency>
I'm trying to configure OAuth2 for a Spring project. I used jdbc authentification and my authorization server and resource server are two separate API. My issue is now with the microservices. I'm trying to use this shared authorization server to authenticate the microservices. I can get access_token from the token endpoint.
I can check the access_token from the check_token endpoint.
My resource server configuration:
#SpringBootApplication
#EnableCircuitBreaker
#EnableDiscoveryClient
#EnableResourceServer
public class ProductApiServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApiServiceApplication.class, args);
}
}
And application.yml:
security:
oauth2:
client:
client-id: saba-product-api-service
client-secret: secret123
resource:
id: saba-product-api-service
token-info-uri: http://localhost:9999/uaa/oauth/check_token
And REST controller:
#GetMapping("/user/me")
public Principal user(Principal principal) {
return principal;
}
When I call the /user/me endpoint I get invalid_token.
My Resource Server log:
And my Authorization Server log:
What is wrong with my code?
Update
The problem is because of this code:
I had the same issue. In my case, I was using spring cloud oauth2, Hoxton.SR4 release and it was working. So, I change to Hoxton.SR6 and the issue was throwed. My Authoriation Server also was a Eureka's client, and the issue was origined cause this dependency. There was one dependĂȘncia inside Eureka Client, named jackson-dataformat-xml, and because it the return of check_token endpoint was converted in xml instead json. When RemoteTokenServices called check_token, and the resulta was a xml, it culdn't decerialized in map<String,Object> the right way. If you had more than one aud, scope or authorities, it picked the last one. And the active propertie was trated as string. In my case I solved the issue excluding in Authorization Server the dependency mentioned from Eureka Client, like this:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
Finally, I replaced
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
with
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.5.0.RELEASE</version>
</dependency>
// gh-838
if (map.containsKey("active") && !"true".equals(String.valueOf(map.get("active")))) {
logger.debug("check_token returned active attribute: " + map.get("active"));
throw new InvalidTokenException(accessToken);
}
I create a real time notification functionality using spring quartz library. I create two services as bellow :
1) quartz-service : Which is used to set schedule a for real time notification.
2) task-service : Which is used to create a task and remind through quartz-service.
When task-service call quartz-service through feign client I'm not get any response. But If I call through Rest Template it's working find.
Actually we are used spring boot microservice architecture, In using Rest Template we need to specify URL Hard coded, So we can't achieved Ribbon concept in this case that's why we not interest to use Rest Template.
So please help me if any once face this problem.
quartz-service :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Rest Controller :
#RestController
#RequestMapping(value = "/quartz/taks", produces = "application/hal+json")
public class QuartzTaskController
{
#Autowired
private QuartzTaskServices quartzTaskServices;
#PostMapping("/reminder")
public ResponseEntity<Object> saveTaskReminder(#RequestBody Task task)
{
quartzTaskServices.saveTaskReminderScheduler(task);
return ResponseEntity.ok().build();
}
}
task-service
Dependency :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
Feign Client :
#RibbonClient(name="quartz-services")
#FeignClient(name="quartz-services")
public interface QuartzProxy
{
#PostMapping("/quartz/taks/reminder")
public ResponseEntity<Object> saveTaskReminder(#RequestBody Task task);
}
Call Feign Client :
#Autowired
private QuartzProxy quartzProxy;
...
.....
......
quartzProxy.saveTaskReminder(task);
Dependencies
org.springframework.cloud:spring-cloud-starter-feign:jar:1.2.2.RELEASE:compile
com.netflix.feign:feign-core:jar:8.16.2:compile
com.netflix.feign:feign-slf4j:jar:8.16.2:compile
com.netflix.feign:feign-jackson:jar:8.15.1:compile
Enabling Feign on SpringBootAppilication
#EnableFeignClients(basePackages = "com.vett.services.bucket.restclient")
Feign interface Client
#FeignClient(name = "myClient", configuration = ClientConfigs.class, url = "https://my-endpoint");
public interface MyClient {
Results in this error
org.springframework.core.annotation.AnnotationConfigurationException: Attribute 'value' in annotation [org.springframework.cloud.netflix.feign.FeignClient] must be declared as an #AliasFor [serviceId], not [name]
So far I have
As its unclear to me what the issue is i have used the value instead of name, my searching has not been successful i have see a few issues with feign annotation but not appear to be similar to this at all
I was getting the same issue, Once I added the below dependency , it started working :
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.SR7"}
}
I am using Spring boot 1.4 but Spring 4.3.6. Also Spring feign 1.2.5.RELEASE
This error may occur when using multiple feign clients or bad package architecture. Sometimes this error occurs due to version incompatibilities, but in some projects we may not be able to change the versions. Therefore, you can solve the problem with the following codes. This codes worked for me.
Use this annotation in ApplicationStarter class:
#EnableFeignClients
Feign Client Interface:
import org.springframework.cloud.netflix.feign.FeignClient;
#FeignClient(value = "account-service", url = "${feign.client.account-service}", path = "/account/api/v1")
public interface AccountServiceClient {
#RequestLine("POST /customer/{email}/?name={accountName}")
Long registerCustomer(#Param("email") String email, #Param("accountName") String accountName);
}
Define bean for multiple feign usage:
#Bean
#Qualifier("account-feign-client")
public AccountServiceClient accountServiceClient() {
return Feign.builder().target( AccountServiceClient.class,"${feign.client.account-service}");
}
#Bean
#Qualifier("mail-feign-client")
public MailServiceClient mailServiceClient() {
return Feign.builder().target( MailServiceClient.class,"${feign.client.mail-service}");
}
Autowire in service:
#Autowired
#Qualifier("account-feign-client")
private AccountServiceClient accountServiceClient;
pom.xml:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
...
</dependencies>