Netty as WebSocket server not starting in SpringBootTest - java

I'm trying to write a simple WebSocket server app based on on Spring and Netty.
My application looks like this
#SpringBootApplication
public class DemoReactiveWSApp {
public static void main(String[] args) {
SpringApplication.run(DemoReactiveWSApp.class, args);
}
}
with the following configuration
#Configuration
public class WebSocketConfig {
#Bean
public HandlerMapping handlerMapping() {
final Map<String, WebSocketHandler> handlerMap = new HashMap<>();
// will be populated later with routes and handlers
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(handlerMap);
mapping.setOrder(-1);
return mapping;
}
#Bean
public RequestUpgradeStrategy requestUpgradeStrategy() {
return new ReactorNettyRequestUpgradeStrategy();
}
}
When I run it, everything boots up, and I can attempt (for now) to establish WS connection.
However, when I want to start it in a test
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoReactiveWSAppTest {
#LocalServerPort
private String port;
#Test
public void givenContext_WhenStartingApplication_ThenItLoads() throws InterruptedException {
System.out.println("Port: " + port);
}
}
the server never seems to boot.
Am I forgetting something?

Stupid me, I forgot #RunWith(SpringRunner.class)

Related

Is it possible to change the Feign Client at runtime?

Let's say I have a FeignClient setup like below with a configuration MyFeignConfiguration.class.
Is it possible to change the feign client(myClient) at runtime as I would like to have different client for different profile(e.g. development and testing)?
#FeignClient(name = "myTestClient", url = "${my.path}", configuration = MyFeignConfiguration.class)
public interface fooClient {
//etc....
}
public class MyFeignConfiguration {
#Bean
public Client myClient() {
return new HttpsClient();
}
}
I use #Profile to configure different clients.
public class MyFeignConfiguration {
#Profile("dev")
#Bean
public Client myDevClient() {
return new DevClient();
}
#Profile("test")
#Bean
public Client myTestClient() {
return new TestClient();
}
}

Configuring RoundRobinLoadBalancer for unit tests

Since Ribbon client side load balancer is in maintenance mode, I migrated to RoundRobinLoadBalancer by setting spring.cloud.loadbalancer.ribbon.enabled to false.
With Ribbon for client tests I used to do
#Configuration
#Profile("test")
public class ClientTestConfig {
#Bean
public StubServer stubServer() {
return new StubServer().run();
}
#Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", stubServer().getPort()));
}
}
Whats the equivalent the code above for RoundRobinLoadBalancer?
I recently did the same switch away from Ribbon. One of the Spring guides has an example of how to configure a custom ServiceInstanceListSupplier:
https://spring.io/guides/gs/spring-cloud-loadbalancer/#_load_balance_across_server_instances
For your case this would probably look something like this:
#Configuration
#Profile("test")
public class ClientTestConfig {
#Bean
public StubServer stubServer() {
return new StubServer().run();
}
#Bean
ServiceInstanceListSupplier serviceInstanceListSupplier() {
return new MyServiceInstanceListSupplier(stubServer().getPort());
}
}
class MyServiceInstanceListSupplier implements ServiceInstanceListSupplier {
private final Integer port;
MyServiceInstanceListSupplier(Integer port) {
this.port = port;
}
#Override
public String getServiceId() {
return "";
}
#Override
public Flux<List<ServiceInstance>> get() {
return Flux.just(Arrays.asList(new DefaultServiceInstance("", "", "localhost", port, false)));
}
}
Notice that I am using empty strings as the instanceId and serviceId. That is probably not the best practice, but works fine in my case.

Spring boot application does not start when ActiveMQ failover transport fails

I want my Spring Boot application to start regardless if it can connect to JMS or not. I have this minimal example:
#SpringBootApplication
#EnableJms
public class JmsActivemqFailoverApplication {
public static void main(String[] args) {
SpringApplication.run(JmsActivemqFailoverApplication.class, args);
}
#Component
public static class JmsReceiver {
#JmsListener(destination = "inbox")
public void receive(Message message) {
System.out.println("Received <" + message + ">");
}
}
#RestController
public static class HelloWorldController {
#GetMapping("/")
public String helloWorld() {
return "Hello world";
}
}
}
when application.properties contains:
spring.activemq.broker-url=tcp://non-existing-broker:61616
I can get response from helloWorld endpoint. When I change property to:
spring.activemq.broker-url=failover:(tcp://non-existing-broker:61616)
Application keeps trying to connect to broker and I can not get response from my REST endpoint.
Please advice, how can I have application running without waiting for ActiveMQ Failover transport to succeed.
Example code available at https://github.com/madoxas/jms-activemq-failover
One way to achieve this is:
Disable automatic JMS container startup with property spring.jms.listener.auto-startup=false
Start JMS container after application has started:
#Component
public class JmsStarter implements ApplicationRunner {
private final JmsListenerEndpointRegistry jmsRegistry;
public JmsStarter(JmsListenerEndpointRegistry jmsRegistry) {
this.jmsRegistry = jmsRegistry;
}
#Override
public void run(ApplicationArguments args) {
for (MessageListenerContainer listenerContainer : jmsRegistry.getListenerContainers()) {
listenerContainer.start();
}
}
}

Spring Integration manually publish message to channel

I'm in the process of learning how to use the Java Spring Framework and started experimenting with Spring Integration. I'm trying to use Spring Integration to connect my application to an MQTT broker both to publish and subscribe to messages but I'm having trouble finding a way to manually publish messages to an outbound channel. If possible I want to build it using notations in the java code exclusively rather than xml files defining beans and other related configuration.
In every example I've seen the solution to manually publishing a message seems to be to use a MessagingGateway Interface and then use the SpringApplicationBuilder to get the ConfigurableApplicationContext to get a reference to the gateway interface in the main method. The reference is then used to publish a message. Would it be possible to use AutoWired for the interface instead? In my attempts I just get a NullPointer.
My aim is to build a game where I subscribe to a topic to get game messages and then whenever the user is ready to make the next move, publish a new message to the topic.
Update:
This is one of the examples I've been looking at of how to setup an outbound channel: https://docs.spring.io/spring-integration/reference/html/mqtt.html
Update 2 after answer from Gary Russel:
This is some example code I wrote after looking at examples which gets me a NullPointer when using #AutoWired for the Gateway when running gateway.sendToMqtt in Controller.java. What I want to achieve here is to send an mqtt message manually when a GET request is handled by the controller.
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
Controller.java
#RestController
#RequestMapping("/publishMessage")
public class Controller {
#Autowired
static Gateway gateway;
#RequestMapping(method = RequestMethod.GET)
public int request(){
gateway.sendToMqtt("Test Message!");
return 0;
}
}
MqttPublisher.java
#EnableIntegration
#Configuration
public class MqttPublisher {
#Bean
public MqttPahoClientFactory mqttClientFactory(){
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setServerURIs("tcp://localhost:1883");
return factory;
}
#Bean
#ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler mqttOutbound(){
MqttPahoMessageHandler messageHandler =
new MqttPahoMessageHandler("clientPublisher", mqttClientFactory());
messageHandler.setAsync(true);
messageHandler.setDefaultTopic("topic");
return messageHandler;
}
#Bean
public MessageChannel mqttOutboundChannel(){
return new DirectChannel();
}
#MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface Gateway {
void sendToMqtt(String data);
}
}
Update:
Not sure if this is the proper logging but it is what I get from adding:
logging.level.org.springframework.web=Debug
logging.level.org.hibernate=Error
to application.properties.
https://hastebin.com/cuvonufeco.hs
Use a Messaging Gateway or simply send a message to the channel.
EDIT
#SpringBootApplication
public class So47846492Application {
public static void main(String[] args) {
SpringApplication.run(So47846492Application.class, args).close();
}
#Bean
public ApplicationRunner runner(MyGate gate) {
return args -> {
gate.send("someTopic", "foo");
Thread.sleep(5_000);
};
}
#Bean
#ServiceActivator(inputChannel = "toMqtt")
public MqttPahoMessageHandler mqtt() {
MqttPahoMessageHandler handler = new MqttPahoMessageHandler("tcp://localhost:1883", "foo",
clientFactory());
handler.setDefaultTopic("myTopic");
handler.setQosExpressionString("1");
return handler;
}
#Bean
public MqttPahoClientFactory clientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setUserName("guest");
factory.setPassword("guest");
return factory;
}
#Bean
public MqttPahoMessageDrivenChannelAdapter mqttIn() {
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "bar", "someTopic");
adapter.setOutputChannelName("fromMqtt");
return adapter;
}
#ServiceActivator(inputChannel = "fromMqtt")
public void in(String in) {
System.out.println(in);
}
#MessagingGateway(defaultRequestChannel = "toMqtt")
public interface MyGate {
void send(#Header(MqttHeaders.TOPIC) String topic, String out);
}
}

Send and get information for Restfull and Spring

I am beginner with spring - boot and webservice, I have two exercises to do, they would know How to implement the client and the run () method; to send and receive strings trough In this webservice?
PROJECT CONSUMER
public class Service implements Runnable {
public static void main(String args[]) {
System.out.println("Send the messages....");
Thread thread = new Thread(new Service());
thread.start();
}
public void run() {
// Loop receiving messages
}
}
PROJECT PRODUCER
#Path("/greet")
#Component
public class GreetResource {
static Logger logger = LoggerFactory.getLogger(GreetResource.class);
#Autowired
Client client;
public GreetResource() {
logger.info("Resource Initialized");
}
#GET
#Path("/echo/{msg}")
#Produces({ MediaType.TEXT_PLAIN_VALUE })
public Response echo(#PathParam("msg") String message) {
return Response.ok().entity(message).build();
}
#POST
#Path("/send")
#Consumes({ MediaType.TEXT_PLAIN_VALUE })
public boolean sendMessage(String greeting) {
client.sendAMessage(greeting);
return true;
}
}
PROJECT PRODUCER
#EnableAutoConfiguration
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Autowired
private Environment env;
#PostConstruct
public void initApplication() throws IOException {
if (env.getActiveProfiles().length == 0) {
logger.warn("No Spring profile configured, running with default configuration");
} else {
logger.info("Running with Spring profile(s) : {}", Arrays.toString(env.getActiveProfiles()));
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
if (activeProfiles.contains("dev") && activeProfiles.contains("prod")) {
logger.error("You have misconfigured your application! "
+ "It should not run with both the 'dev' and 'prod' profiles at the same time.");
}
}
}
public static void main(String[] args) {
logger.info("weather application service starting...");
SpringApplication.run(Application.class, args);
}
}
PROJECT PRODUCER
import org.springframework.stereotype.Component;
#Component
public class Client {
public boolean sendAMessage(String greeting) {
// Send the message
return false;
}
}
Any tips to implement method run () and sendAMessage()?
If you are using Spring Boot and want to implement web-service , then easiest way is to implement web service using RESTapi.
You can use #ResponseBody or #RestController annotations to expose service.

Categories

Resources