How to provide custom SSLContext to Netty server on spring boot - spring-boot

How can we configure a custom SSLContext to a spring boot application with Netty server?
From the source code, I see 'reactor.ipc.netty.http.server.HttpServerOptions' which are some server startup options, but I don't find a way to configure them.
Is there any handler through which we can inject our custom SSLContext?
I am looking something similar to this (Spring 5 WebClient using ssl) where WebClient is configured with a custom SSLContext through 'reactor.ipc.netty.http.client.HttpClientOptions'.

Netty can be customized like blow example in spring-boot 2.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
/**
* author : Mohammad Ghoreishi
*/
#Configuration
#ImportResource({"classpath:convert-iban-service.xml", "classpath:config-loader-context.xml", "classpath*:error-resolver.xml"})
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Bean
public WebServerFactoryCustomizer<NettyReactiveWebServerFactory> customizer(){
return new WebServerFactoryCustomizer<NettyReactiveWebServerFactory>() {
#Override
public void customize(NettyReactiveWebServerFactory factory) {
Ssl ssl = new Ssl();
// Your SSL Cusomizations
ssl.setEnabled(true);
ssl.setKeyStore("/path/to/keystore/keystore.jks");
ssl.setKeyAlias("alias");
ssl.setKeyPassword("password");
factory.setSsl(ssl);
factory.addErrorPages(new ErrorPage("/errorPage"));
}
};
}
}

Related

Spring Cloud APIGW and Open API Specification Integration issues

I am looking to develop a Spring Cloud APIGW and Open API Specification Integration example. I am following: https://piotrminkowski.com/2018/04/26/quick-guide-to-microservices-with-spring-boot-2-0-eureka-and-spring-cloud/, but use all latest dependency of Spring Boot which is 2.7.0
http://localhost:8011/swagger-ui.html is not working
gateway.properties
server.port=8011
spring.application.name=api-gateway
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
spring.config.import=configserver:http://localhost:8012/
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
# Actuator
# https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.enabling
management.endpoint.gateway.enabled=true
management.endpoints.web.exposure.include=beans, health, metrics, mappings, env, info,configprops, caches, gateway
# All Microservices Routes
spring.cloud.gateway.routes[0].id=organization-service
spring.cloud.gateway.routes[0].uri=lb://organization-service
spring.cloud.gateway.routes[0].predicates[0]=Path=/organization/**
spring.cloud.gateway.routes[0].predicates[1]=Method=GET,POST,PUT,DELETE
spring.cloud.gateway.routes[1].id=department-service
spring.cloud.gateway.routes[1].uri=lb://department-service
spring.cloud.gateway.routes[1].predicates[0]=Path=/department/**
spring.cloud.gateway.routes[1].predicates[1]=Method=GET,POST,PUT,DELETE
spring.cloud.gateway.routes[2].id=employee-service
spring.cloud.gateway.routes[2].uri=lb://employee-service
spring.cloud.gateway.routes[2].predicates[0]=Path=/employee/**
spring.cloud.gateway.routes[2].predicates[1]=Method=GET,POST,PUT,DELETE
spring.cloud.gateway.routes[3].id=openapi
spring.cloud.gateway.routes[3].uri=http://localhost:${server.port}
spring.cloud.gateway.routes[3].predicates[0]=Path=/v3/api-docs/**
spring.cloud.gateway.routes[3].predicates[1]=Method=GET,POST,PUT,DELETE
spring.cloud.gateway.routes[4].id=openapi2
spring.cloud.gateway.routes[4].uri=http://localhost:${server.port}
spring.cloud.gateway.routes[4].predicates[0]=Path=/webjars/**
spring.cloud.gateway.routes[4].predicates[1]=Method=GET,POST,PUT,DELETE
# Open API Specification
springdoc.swagger-ui.urls[0].name=employee
springdoc.swagger-ui.urls[0].url=/v3/api-docs/employee
springdoc.swagger-ui.urls[1].name=department
springdoc.swagger-ui.urls[1].url=/v3/api-docs/department
springdoc.swagger-ui.urls[2].name=organization
springdoc.swagger-ui.urls[2].url=/v3/api-docs/organization
server.max-http-header-size=2000000
MainApp.java
package com.example.demo;
import java.util.ArrayList;
import java.util.List;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
#EnableEurekaClient
#SpringBootApplication
public class GatewayServiceApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServiceApplication.class, args);
}
#Autowired
RouteDefinitionLocator locator;
#Bean
public List<GroupedOpenApi> apis() {
List<GroupedOpenApi> groups = new ArrayList<>();
List<RouteDefinition> definitions = locator.getRouteDefinitions().collectList().block();
assert definitions != null;
definitions.stream().filter(routeDefinition -> routeDefinition.getId().matches(".*-service")).forEach(routeDefinition -> {
String name = routeDefinition.getId().replaceAll("-service", "");
groups.add(GroupedOpenApi.builder().pathsToMatch("/" + name + "/**").group(name).build());
});
return groups;
}
}
Note: I am showing for 1 microservice, I've done almost same for others
application.properties
server.port=${PORT:0}
spring.application.name=employee-service
spring.config.import=configserver:http://localhost:8012/
eureka.client.fetch-registry=true
eureka.client.register-with-eureka=true
springdoc.packages-to-scan=com.example.demo
and Added below to entry service main app.
MainApp.java
#OpenAPIDefinition(info = #Info(title = "Employee API", version = "1.0", description = "Documentation Employee API v1.0"))
#EnableEurekaClient
#SpringBootApplication
public class EmployeeServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EmployeeServiceApplication.class, args);
}
}

Debug Spring Boot application in VSC

I have one simple Spring boot app that runs normally in console.
But I can't debug it in VSC.
The code is:
package com.sample.service.sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
/**
* sample service
*
*/
#EnableDiscoveryClient
#SpringBootApplication
#EnableBinding(Source.class)
#EnableMongoRepositories(basePackages = "com.sample.service.sample.repositories")
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
The exception is:
nested exception is java.lang.NoClassDefFoundError: org/objenesis/strategy/InstantiatorStrategy
What did I miss in VSC setting for Spring Boot?

How to exclude some endpoints from server.servlet.path configuration?

I have Spring Boot application with a single index.html page.
I need to have server.servlet.path=/api setting.
In order to get index.html I have to go localhost:8080/api/ becase of my setting described above.
I want to be able to get index.html by localhost:8080/ and any else endpoints by localhost:8080/api/**.
How can I do it?
Thanks
Once you configured server.servlet.path=/api, DispatcherServlet is going to handle only request matching URL Patterns /api/**.
One way to achieve your requirement is to use a plain Servlet.
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StreamUtils;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;
#WebServlet(urlPatterns = {"/"})
public class RootServlet extends HttpServlet {
#Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
ClassPathResource resource = new ClassPathResource("static/index.html");
String content = StreamUtils.copyToString(resource.getInputStream(), Charset.defaultCharset() );
resp.getWriter().write(content);
}
}
Now you can register the Servlet using #ServletComponentScan annotation. Assuming you put RootServlet in com.myapp.servlets package:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
#SpringBootApplication
#ServletComponentScan(basePackages = "com.myapp.servlets")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
For more info see https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-add-a-servlet-filter-or-listener

Spring Integration HTTP Outbound Gateway Post Request with Java DSL

I am trying to consume a rest service and receive a json back and convert it to a list of objects. but I am receiving the below erorr. I am new to EIP and there aren't many tutorials for doing this in java dsl. I have configured 2 channels, one for sending a request and one for receiving the payload back.
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'httpPostAtms' is expected to be of type 'org.springframework.messaging.MessageChannel' but was actually of type 'org.springframework.integration.dsl.StandardIntegrationFlow'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:378)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.integration.support.channel.BeanFactoryChannelResolver.resolveDestination(BeanFactoryChannelResolver.java:89)
at org.springframework.integration.support.channel.BeanFactoryChannelResolver.resolveDestination(BeanFactoryChannelResolver.java:46)
at org.springframework.integration.gateway.MessagingGatewaySupport.getRequestChannel(MessagingGatewaySupport.java:344)
at org.springframework.integration.gateway.MessagingGatewaySupport.doSendAndReceive(MessagingGatewaySupport.java:433)
at org.springframework.integration.gateway.MessagingGatewaySupport.sendAndReceive(MessagingGatewaySupport.java:422)
at org.springframework.integration.gateway.GatewayProxyFactoryBean.invokeGatewayMethod(GatewayProxyFactoryBean.java:474)
at org.springframework.integration.gateway.GatewayProxyFactoryBean.doInvoke(GatewayProxyFactoryBean.java:429)
at org.springframework.integration.gateway.GatewayProxyFactoryBean.invoke(GatewayProxyFactoryBean.java:420)
at org.springframework.integration.gateway.GatewayCompletableFutureProxyFactoryBean.invoke(GatewayCompletableFutureProxyFactoryBean.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy70.getAllAtms(Unknown Source)
at com.backbase.atm.IngAtmApplication.main(IngAtmApplication.java:25)
I am using SI with Spring Boot
#IntegrationComponentScan
#Configuration
#EnableIntegration
#ComponentScan
public class InfrastructorConfig {
#Bean
public PollableChannel requestChannel() {
return new PriorityChannel() ;
}
#Bean
public MessageChannel replyChannel() {
return new DirectChannel() ;
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller() {
return Pollers.fixedRate(500).get();
}
#Bean
public IntegrationFlow httpPostAtms() {
return IntegrationFlows.from("requestChannel")
.handle(Http.outboundGateway("https://www.ing.nl/api/locator/atms/")
.httpMethod(HttpMethod.POST)
.extractPayload(true))
.<String, String>transform(p -> p.substring(5))
.transform(Transformers.fromJson(Atm[].class))
.channel("responseChannel")
.get();
}
}
The Gateway
package com.backbase.atm.service;
import java.util.List;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.messaging.handler.annotation.Payload;
import com.backbase.atm.model.Atm;
#MessagingGateway
public interface IntegrationService {
#Gateway(requestChannel = "httpPostAtms")
#Payload("new java.util.Date()")
List<Atm> getAllAtms();
}
Application Start
package com.backbase.atm;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import com.backbase.atm.service.IntegrationService;
#SpringBootApplication
public class IngAtmApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(IngAtmApplication.class, args);
ctx.getBean(IntegrationService.class).getAllAtms();
ctx.close();
}
You have to use requestChannel bean name in the gateway definition. Right now you have there an IntegrationFlow bean name, but that is wrong.
Always remember that everything in Spring Integration are connected via channels.

Spring Boot Apache Artemis Embedded JMS Queue Eample

I am trying to set up a simple Spring Boot application that uses an embedded JMS Queue. I am successful with HornetQ but when I try to convert to Artemis I am getting a failure on the ArtemisConnectionFactory. Here is my code that I use for HornetQ. Any help would be appreciative.
package com.comporium.log.server;
import javax.jms.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import com.comporium.log.server.services.LogListener;
#SpringBootApplication
public class Application {
#Autowired
private ConnectionFactory connectionFactory;
#Autowired
LogListener logListener;
#Bean
public DefaultMessageListenerContainer messageListener() {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(this.connectionFactory);
container.setDestinationName("loggerQueue");
container.setMessageListener(logListener);
return container;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
For me your code worked. To test the application I have added a CommandLineRunner which produces a message.
#Bean
CommandLineRunner sendMessage(JmsTemplate jmsTemplate) {
return args -> {
jmsTemplate.convertAndSend("loggerQueue", "Message to Artemis");
};
}
The consumer will consume the message sent to this queue. It it not necessary to declare any properties, but I have defined the following compile time dependencies on my project:
compile('org.springframework.boot:spring-boot-starter-artemis')
compile('org.apache.activemq:artemis-jms-server')

Resources