I have a system of several spring-boot 2.1.x applications, communicating over https. The hostnames are generated at runtime, according to a naming schema, with variable suffixes - and the IP addresses are also generated at runtime, in an OpenShift environment. The idea is that the certificate CNs don't match the runtime hostnames.
The http client that I am using in these applications is netty.
I want to be able to use my custom implementation of the HostnameVerifier interface - or any other similar method, but I don't know how to get a handle on the netty client configuration from Spring environment:
public class TrustAllHostnames implements HostnameVerifier {
#Override
public boolean verify(String hostname, SSLSession sslSession) {
// incomplete
return true;
}
}
Related
I have a spring boot project which is having two Feign Client's.
First Feign client (i.e. ServiceAProxy.class below), which will work on Service Discovery using Eureka or Consul.
#FeignClient(name="service-A")
public interface ServiceAProxy{
#RequestMapping(method= RequestMethod.GET)
String getResponse();
}
Second Feign client( i.e ServiceBProxy.class ), needs to have a set of server list which will be picked from Spring Cloud Configuration server or Apache zookeeper config server and will be passed to the ribbon client of ServiceBProxy.class. For that, I'm trying to add a Ribbon Client Configuration that will modify the ribbon server list. Below is the code for the second feign client.
#FeignClient(name="service-B")
#RibbonClient(name="service-B",configuration= ServiceBProxy.LocalRibbonClientConfig.class)
public interface ServiceBProxy{
#RequestMapping(method = RequestMethod.GET)
String invalidateUsers();
#Configuration
class LocalRibbonClientConfig{
#Value("${server-host1}") // fetched from config
private String host1;
#Value("${server-host2}") // fetched from config
private String host2;
#Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(
new Server(host1,8080),
new Server(host2,8081)
);
}
}
}
Now, when I run the code, ServiceBProxy.class works as expected and picks the list of servers that was specified in the LocalRibbonClientConfig.class.
But the problem is with the ServiceAProxy.class which was suppose to work on the basis of service discovery also starts to use the LocalRibbonClientConfig.
How can I only allow ServiceBProxy.class to use the custom ribbon configurations and other feign clients in the project to work as per their default behaviour.
Please guide on this.
I'm developing a service that has many dependencies like Redis, PubSub, S3, ServiceC and ServiceD. When I hit an endpoint in development, e.g. /data/4, a http request to ServiceC is performed.
So to that to work, something that mocks ServiceC must run. In my case it's wiremock (since I cant run ServiceC directly, as it also has many dependencies). All of these MockServices are in a docker-compose-dev file.
Now to my question: How can I run the docker-compose with testcontainers, get the assigned ports, and set the correct properties to the WebClient has the right mock url + port?
What lifecycle hook can I use to run before spring boot starts, and also can configure the properties?
One downside would be an increased boot time in the dev mode though, but I can't assign fixed ports in docker compose file, because they might be used on the developer's machine. So makes no sense to ship url defaults for the service urls on localhost.
Testcontainers supports starting Docker Compose out-of-the-box using the DockerComposeContainer class. While creating an instance of this class you have to expose all your containers:
#Testcontainers
public class DockerComposeTest {
#Container
public static DockerComposeContainer<?> environment =
new DockerComposeContainer<>(new File("docker-compose.yml"))
.withExposedService("database_1", 5432, Wait.forListeningPort())
.withExposedService("keycloak_1", 8080,
Wait.forHttp("/auth").forStatusCode(200)
.withStartupTimeout(Duration.ofSeconds(30)));
#Test
void dockerComposeTest() {
System.out.println(environment.getServicePort("database_1", 5432));
System.out.println(environment.getServicePort("keycloak_1", 8080));
}
}
The example above maps a PostgreSQL database and Keycloak to a random ephemeral port on your machine. Right after the wait checks pass, your test will run and you can access the actual port with .getServicePort().
A possible solution for the WebClient configuration is to use a ApplicationContextInitializer as you somehow have to define the Spring Boot property before the container starts:
public class PropertyInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
// static access to environment variable of the test
TestPropertyValues
.of("your_web_client_base_url=http://localhost:" + environment.getServicePort("serviceC", 8080) + "/api")
.applyTo(configurableApplicationContext);
}
}
You can then register the initializer for your integration tests with:
#Testcontainers
#ContextConfiguration(initializers = {PropertyInitializer.class})
class DockerComposeTest {
}
As an alternative, you could also try to solely mock all HTTP communication to external services with WireMock.
I am attempting to send data through IOWebSocketChannel in Flutter.io to a WebSocket created in Spring-Boot.
In spring-boot I have created the typical WebSocket config and controllers that are dealing with client's manipulation of my servers WebSocket. I will post them below just for reference.
WebSocketConfiguration.java
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry){
registry.addEndpoint("/websocket")
.setAllowedOrigins("*") // allow us to connect to ws://localhost:8080/websocket with the default Spring port configuration.
.withSockJS(); // allows a client which does not support WebSocket natively mimic a WebSocket over an HTTP connection
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry){ //The configureMessageBroker method sets up a simple (in-memory) message broker for our application
registry.enableSimpleBroker("/topic"); //topic to be routed back to client
registry.setApplicationDestinationPrefixes("/app"); //This configuration allows Spring to understand that any message sent to a WebSocket channel name prefixed with /app should be routed to a #MessageMapping in our application.
}
}
WebSocketController.java
#Controller
public class WebSocketController {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketController.class);
#MessageMapping("/send")
#SendTo("/topic/messages")
public Message send(Message message) {
LOGGER.info(String.format("Received message [%s]", message.toString()));
LocalDateTime timestamp = LocalDateTime.now();
return new Message(message.getFrom(), message.getMessage(), timestamp);
}
}
Now When I try using IOWebSocketChannel I perform the typical protocol of connecting to my configured websocket. Below is the code
final channel = IOWebSocketChannel.connect(
"ws://10.0.2.2:8080/websocket"
);
I have then created a method that is supposed to send data to my websocket so I attempt to connect to that endpoint which you see is created in WebSocketController.java called app/send/. Below is the code:
void _sendMessage() {
IOWebSocketChannel channel = IOWebSocketChannel.connect('ws://10.0.2.2:8080/app/send');
channel.sink.add(
json.encode({
"message": "bars",
})
);
}
Now when I check my Spring-Boot server nothing is logged, however, whenever I hot reload in Flutter Spring Boot and my connection to the websocket times out, tomcat server returns this:
So my question is if anybody has been able to make a breakthrough with sending data through websockets from Flutter into Spring-Boot using IOWebSocketChannel? I am also wondering if anyone has found a way to successfully use a STOMP protocol in Flutter.io? I was using stomp_client as it seemed like it was going to do the trick, however correct if I'm wrong, but flutter was giving me errors saying that there doesn't exist any html files, so I'm assuming that library is only for dart in the web.
Your Spring configuration looks good. But client-side you need some tweaks.
I spent some time to figure this out with the https://pub.dev/packages/stomp package. Use a modified version of the connect function provided here. Make sure to use this custom implementation of the connect function.
Future<StompClient> client = customStomp.connect('ws://10.0.2.2:8080/websocket', ...)
Once connected, according to your configuration, you can then send message on the following destination: /app/send.
I'd like to ask. How to pass binary data between microservices in Spring Cloud?
Should (Can) I use #FeignClient or #RibbonClient ? How it should be? I've already read that #FeignClient is not deal with this issue What else? OkHttp?
Thx in advance
Spring Cloud integrates with some http clients, like you mentioned. Ribbon has some non-http clients/transports built in, but I haven't used that and AFIAK, netflix doesn't use them either.
You can use the Spring Cloud LoadBalancerClient interface directly. It gives you access to a host and port, then you can use any client you want.
public class MyClass {
#Autowired
private LoadBalancerClient loadBalancer;
public void doStuff() {
ServiceInstance instance = loadBalancer.choose("myService");
String host = instance.getHost();
int port = instance.getPort();
// ... do something with the host and port
}
}
I also did a sample integration with OkHttp.
We want to use Spring Cloud for AWS SQS, but it seems to me that it only allows us to specify region. Can we fake it so that it uses ElasticMQ (on localhost:9320 for instance)? I didn't find an easy way to do this without editing hosts file and putting certificates on localhost
I found a way after some research.
You should set an endpoint after AmazonSQS instance is injected in order to override the already set endpoint, as so:
#Autowired
public void setAmazonSqs(AmazonSQS amazonSqs)
{
this.amazonSqs = amazonSqs;
// use elasticMQ if spring default profile is used; no active profiles
if (environment.getActiveProfiles().length == 0)
{
amazonSqs.setEndpoint("http://localhost:9320");
}
}
it is up to you if you're going to use QueueMessagingTemplate, anyway you should modify the injected AmazonSQS instance.