I'd like to make a RESTful web app with Spring Boot that would be constantly receiving information from a websocket connection, aggregating it, and serving those aggregated data via its REST API.
So I'll need to open and maintain a WebSocket connection, while also running SpringApplication.run() in the main() function.
The #Scheduled annotation seems to only run tasks at a specific time or interval, but doesn't seem to have a way to always run something in the background.
What is a sensible way to achieve what I've described?
Just have your application connect to a socket on application startup
#SpringBootApplication
public class SpringBootConsoleApplication
implements CommandLineRunner {
private static Logger LOG = LoggerFactory
.getLogger(SpringBootConsoleApplication.class);
public static void main(String[] args) {
LOG.info("STARTING THE APPLICATION");
SpringApplication.run(SpringBootConsoleApplication.class, args);
LOG.info("APPLICATION FINISHED");
}
#Override
public void run(String... args) {
//Connect to socket and cache the results if you want to
}}
Spring Boot will automatically call the run method of all beans implementing this interface after the application context has been loaded
for more information about command line runner you can visit https://www.baeldung.com/spring-boot-console-app
Why not use the WebSocketClient provided by Spring? After you connect you will get notified via a callback method each time a new websocket frame is received.
You can follow the example here https://www.baeldung.com/websockets-api-java-spring-client
Just put it in a class annotated with #Component or #Service that starts the websocket session through the constructor.
You won't need to do the new Scanner(System.in).nextLine() since you have a server that is always running.
Related
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 am using Quarkus to build an application that is running on the Raspberry PI. Quarkus exposes Rest interfaces to control the PI; once Quarkus is started, Chrome is launched to serve a HTML/Javascript UI packaged within the Quarkus application.
The problem I have is that as the PI is quiet slow, I notice that when I start the browser to open the UI, the HTTP port seems not to be open yet or is not accepting any Rest requests yet and thus gets back 404. A seconds later, the port is available.
I am not sure what is the best way to detect the readiness of the Webcontainer.
I tried with this but it did not work and fires too early:
#WebListener
public class BrowserMain implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
// Start Chrome
}
}
Thanks,
Daniel
Quarkus integrates with MicroProfile Health which should do exactly what you are looking for. See this guide for more details.
If what you are looking for is a way to programmatically know when the application is ready, then you simply have to listen for the StartupEvent like so:
#ApplicationScoped
public class AppLifecycleBean {
private static final Logger LOGGER = LoggerFactory.getLogger("ListenerBean");
void onStart(#Observes StartupEvent ev) {
LOGGER.info("The application is starting...");
}
void onStop(#Observes ShutdownEvent ev) {
LOGGER.info("The application is stopping...");
}
}
To see more details check out this guide.
What is the equivalent of MockHttpServletRequestBuilder for websockets. i.e. In a situation where I want to test Websockets., I would like to test a long running websocket application and avoid the situation where SecurityContextPersistenceFilter is overriding the SecurityContex after the first http get call that is supposed to do the upgrade. For plain rest http apps this was done so far by leveraging the SecurityMockMvcRequestPostProcessors.
example here using the SecurityMockMvcRequestPostProcessors
But what to do when I want to test a long running websocket application. i.e. I want ot create something like MockHttpServletRequestBuilder for websockets. Does spring have something like that already? Or is there a way to use MockHttpServletRequestBuilder for that purpose? I.e. the target is to create the websocket endpoint and avoid the situation where the SecurityContex is beeing cleared after the upgrade.
I have found some alternatives such as passing the session as described here but this is not really an alternative for me as then the code that is using method level security does not work since the SecurityContex is being altered.
It appears that this can be done by providing a test sock config. Ex
#EnableWebSocketMessageBroker
static class TestWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Autowired
Environment env;
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// registry.enableSimpleBroker("/queue/", "/topic/");
registry.enableStompBrokerRelay("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
}
The full class can be found here:
https://github.com/rstoyanchev/spring-websocket-portfolio/blob/master/src/test/java/org/springframework/samples/portfolio/web/context/ContextPortfolioControllerTests.java
Here are also some additional examples provided by spring the demonstrate 3 different approaches to testing web sockets:
https://github.com/rstoyanchev/spring-websocket-portfolio/tree/master/src/test/java/org/springframework/samples/portfolio/web
I have a requirement where by the application will need to be periodically refreshed to subscribe from different end points. I am using Apache Camel for orchestration and I am comfortable subscribing to an end point.
I have a routebuilder class as follows:
public class SampleRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
String... subscriptionTopicUris = someService.getUris();
// Simple logic - read from URIs and write to topic
from(subscriptionTopicUris) //
.to(destinationUri) //
.routeId("SAMPLE_ROUTE_ID");
}
}
I run a scheduled job with a given time interval and I remove the route from Camel Context, stop the context and add it back and start the context. However, the configure method is not fired on starting the context and hence the service (someService) possibly returning a different list of URIs is never fired.
How do I reload the route?
I am using Spring, Apache Camel and annotation based approach and this is a web application running in jetty.
Thanks
I'm using Spring Boot and I've got a use case where user can upload a file which should cause a restart of application (since user's upload is used during creation of multiple beans). I know I can avoid restarting the application, but at the moment - this is what I want.
I've found RestartEndpoint in Spring-Cloud project, but it doesn't seem like ApplicationPreparedEvent is fired. Is there any other way I can programmatically restart Spring Boot application?
The simplest way to do this by calling the refresh() method on the Spring ApplicationContext. This will kill and reload all of your beans, so you should be certain that this occurs only when it is safe for your application to do so.
I have a special case in which my Spring Boot application, which runs on Linux, is required to run as root.
Since I run the application as systemd service, I can restart it like this:
Runtime.getRuntime().exec(new String[] { "/bin/systemctl", "restart", "foo" });
If your application is (hopefully) not required to run as root, a setuid wrapper-script could be used, or better, use sudo.
Check this answer on how to do that:
https://superuser.com/questions/440363/can-i-make-a-script-always-execute-as-root
In your case it might be possible to use the /refresh endpoint (see http://cloud.spring.io/spring-cloud-config/spring-cloud-config.html#_endpoints) and annotate the beans that depend on the changed configuration with #RefreshScope.
I used the below code to restart my application from the code itself. Closing context using a separate thread will not shut the JVM.
public class DemoApplication {
private static String[] args;
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
DemoApplication.args = args;
context = SpringApplication.run(DemoApplication.class, args);
}
public static void restart() {
Thread thread = new Thread(() -> {
context.close();
context = SpringApplication.run(DemoApplication.class, DemoApplication.args);
});
thread.setDaemon(false);
thread.start();
}