Probably something simple, but I can't figure it out on my own. I have some sample of Spring Boot WebSockets implementation and wanted to display total active sessions. So I created #Scheduled activeSessions task, which should display actual count, but it's always 0. When afterConnectionEstablished is called I get expected sessions size. Whats the catch?
#Configuration
public class Monitoring extends TextWebSocketHandler {
private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message)
throws Exception {
String clientMessage = message.getPayload();
System.out.println(clientMessage);
sessions.forEach(s -> {
try {
s.sendMessage(new TextMessage("Hello! You session id is: " + s.getId()));
activeSessions();
} catch (IOException e) {
e.printStackTrace();
}
});
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//the messages will be broadcasted to all users.
System.out.println("Adding new session.");
sessions.add(session);
System.out.println("Current session count: " + sessions.size());
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session);
}
#Scheduled(fixedRate = 2000)
public void activeSessions() {
System.out.println("Total sessions: " + sessions.size());
}
}
Configuration part:
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(#NotNull WebSocketHandlerRegistry registry) {
registry.addHandler(new Monitoring(), "/socket");
}
}
Due conflict described, custom scheduler:
#Configuration
#EnableScheduling
public class SchedulingConfig {
// https://stackoverflow.com/questions/49343692/websocketconfigurer-and-scheduled-are-not-work-well-in-an-application
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(10);
taskScheduler.initialize();
return taskScheduler;
}
}
You currently have 2 separate instances of the Monitoring class. One created by yourself, doing the request handling (which isn't a Spring managed bean!) and another one detected by Spring due to the #Configuration (shouldn't that be an #Component?).
Remove the #Configuration and replace it with an #Bean method, such that your WebSocketConfig looks like the following
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(#NotNull WebSocketHandlerRegistry registry) {
registry.addHandler(monitoring(), "/socket");
}
#Bean
public Monitoring monitoring() {
return new Monitoring();
}
}
You now have a single instance of the bean, managed by Spring.
Related
Here I have 3 Interfaces: InterfaceA and InterfaceB and SharedInterface
public interface InterfaceA {
/**
* print some message
*/
void printMsg();
}
public interface InterfaceB {
/**
* print some message
*/
void printMsg();
}
public interface SharedInterface {
/**
* print some message
*/
void printSharedMsg();
}
and there are 3 implementations of these interfaces:
public class ImplementA1 implements InterfaceA, SharedInterface {
#Override
public void printMsg() {
System.out.println("this is message of interfaceA1");
}
#Override
public void printSharedMsg() {
System.out.println("this is shared message from ImplementA1");
}
}
public class ImplementA2 implements InterfaceA, SharedInterface {
#Override
public void printMsg() {
System.out.println("this is message of interfaceA2");
}
#Override
public void printSharedMsg() {
System.out.println("this is shared message from ImplementA2");
}
}
public class ImplementB implements InterfaceB, SharedInterface {
#Override
public void printMsg() {
System.out.println("this is message of interfaceB");
}
#Override
public void printSharedMsg() {
System.out.println("this is shared message from ImplementB");
}
}
ImplementA1 and ImplementA2 are the same type of operation, ImplementB is another type of operation. So I decided to develop 2 config class to register ImplementA1,ImplementA2 and ImplementB which is showing below.
#Configuration
public class InterfaceAConfig {
#Bean
public InterfaceA registerInterfaceA1(){
return new ImplementA1();
}
#Bean
public InterfaceA registerInterfaceA2(){
return new ImplementA2();
}
}
#Configuration
public class InterfaceBConfig {
#Bean
public InterfaceB registerInterfaceB(){
return new ImplementB();
}
}
Now I want to let all beans which implement SharedInterface print their message in a component. And it works well,here is the code:
#Component
#AutoConfigureAfter(value = {
InterfaceAConfig.class,
InterfaceBConfig.class})
public class SharedInterfaceComponent implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
private ApplicationContext applicationContext;
//print shared message after IOC container refreshed
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
usingContextGetBean();
}
private void usingContextGetBean() {
Map<String, SharedInterface> beans = this.applicationContext.getBeansOfType(SharedInterface.class);
System.out.println(beans.size());
for (SharedInterface bean : beans.values()) {
bean.printSharedMsg();
}
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
But I found another way of inject beans to component, using
#Autowired
List<TargetType> myListName
So I decided to change my SharedInterfaceComponent to this for test, and it worked:
#Component
#AutoConfigureAfter(value = {
InterfaceAConfig.class,
InterfaceBConfig.class})
public class SharedInterfaceComponent implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
private ApplicationContext applicationContext;
//todo why do spring failed due to this autowire?
#Autowired
private List<InterfaceA> autowiredList;
//print shared message after IOC container refreshed
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
usingAutowiredGerBean();
//usingContextGetBean();
}
private void usingAutowiredGerBean() {
for (InterfaceA interfaceA : autowiredList) {
if (SharedInterface.class.isAssignableFrom(interfaceA.getClass())){
((SharedInterface) interfaceA).printSharedMsg();
}
}
}
private void usingContextGetBean() {
Map<String, SharedInterface> beans = this.applicationContext.getBeansOfType(SharedInterface.class);
System.out.println(beans.size());
for (SharedInterface bean : beans.values()) {
bean.printSharedMsg();
}
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
But when I tried to use SharedInterface instead of InerfaceA to get beans from IOC , it goes wrong. The code is showing below:
#Component
#AutoConfigureAfter(value = {
InterfaceAConfig.class,
InterfaceBConfig.class})
public class SharedInterfaceComponent implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
private ApplicationContext applicationContext;
//todo why do spring failed due to this autowire?
#Autowired
private List<SharedInterface> autowiredList;
//print shared message after IOC container refreshed
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
usingAutowiredGerBean();
//usingContextGetBean();
}
private void usingAutowiredGerBean() {
for (SharedInterface sharedInterface : autowiredList) {
if (SharedInterface.class.isAssignableFrom(sharedInterface.getClass())){
((SharedInterface) sharedInterface).printSharedMsg();
}
}
}
private void usingContextGetBean() {
Map<String, SharedInterface> beans = this.applicationContext.getBeansOfType(SharedInterface.class);
System.out.println(beans.size());
for (SharedInterface bean : beans.values()) {
bean.printSharedMsg();
}
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
In this Demo the application will fail and showing
***************************
APPLICATION FAILED TO START
***************************
Description:
Field autowiredList in com.wwstation.test.config.SharedInterfaceComponent required a bean of type 'java.util.List' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'java.util.List' in your configuration.
But in my other projects, the same situation will not lead to a crush, I can get SharedInterface by using #Autowired but there I can only get beans implement InterfaceA or InterfaceB but never all of them. I thought, the not crushing case may be caused by some of my dependencies in other projects.
Can anyone help me about how to get all the SharedInterface more graceful? Thanks alot!
The problem is your configuration.
#Bean
public InterfaceA registerInterfaceA1(){
return new ImplementA1();
}
The problem with this is that Spring will use the return type of the method to see if it fullfils injection points (in this case your list). As InterfaceA isn't a SharedInterface eventually it will fail as there are no beans that implement the SharedInterface according to your configuration!.
What you should do with your own beans is to be as specific as possible in the return type. So instead of InterfaceA make it return the actual class ImplementA1 and ImplementA2. That way Spring, at configuration time, can determine that those implement SharedInterface and use those to fill the list.
#Bean
public ImplementA1 registerInterfaceA1(){
return new ImplementA1();
}
I have my authorization server working with the autoconfiguration of spring-oauth2 #EnableAuthorizationServer in which I have my own custom ClientDetailService service, I am doing very well but I have a problem, it turns out that said service runs 6 times when invoking the endpoint [ oauth / token], which I am not sure is normal behavior but I want it to be executed only once because in it I call my database. Please your support.
My configuration:
#Configuration
#EnableAuthorizationServer
#Slf4j
public class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
#Autowired
private ApplicationJwtAccessTokenConverter applicationJwtAccessTokenConverter;
#Autowired
private ApplicationOauth2Service applicationOauth2Service;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(applicationOauth2Service);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenEnhancer(applicationJwtAccessTokenConverter)
.tokenStore(jwtTokenStore());
}
#Bean
public JwtTokenStore jwtTokenStore() {
return new JwtTokenStore(applicationJwtAccessTokenConverter);
}
}
My ClientDetailService:
#Service
#Slf4j
public class ApplicationOauth2Service implements ClientDetailsService {
#Autowired
UserRepository userRepository;
User user;
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
BaseClientDetails details = new BaseClientDetails();
details.setAuthorizedGrantTypes(Collections.singletonList("client_credentials"));
user = userRepository.findByEmail(clientId);
if (null == user) {
throw new NoSuchClientException("No client with requested id: " + clientId);
}
details.setClientId(user.getAuth().getEmail());
details.setClientSecret(user.getAuth().getPassword());
return details;
}
}
My console output:
I have to add support for a custom WebSocket subprotocol (so not STOMP) in a Spring Boot application, but I'm having a very hard time understanding what I need to provide and what Spring already has.
This is how far I got:
#Configuration
#EnableWebSocket
public class WebSocketAutoConfiguration implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(this.webSocketHandler(), new String[]{endpointUrl});
}
#Bean
public WebSocketHandler webSocketHandler() {
ExecutorSubscribableChannel clientInboundChannel = new ExecutorSubscribableChannel();
ExecutorSubscribableChannel clientOutboundChannel = new ExecutorSubscribableChannel();
SubProtocolWebSocketHandler subProtocolWebSocketHandler = new SubProtocolWebSocketHandler(clientInboundChannel, clientOutboundChannel);
subProtocolWebSocketHandler.addProtocolHandler(new SubProtocolHandler() {
public List<String> getSupportedProtocols() {
return Collections.singletonList("custom-protocol");
}
public void handleMessageFromClient(WebSocketSession session, WebSocketMessage<?> message, MessageChannel outputChannel) throws Exception {
session.sendMessage(new TextMessage("some message"));
}
public void handleMessageToClient(WebSocketSession session, Message<?> message) throws Exception {
}
public String resolveSessionId(Message<?> message) {
return UUID.randomUUID().toString();
}
public void afterSessionStarted(WebSocketSession session, MessageChannel outputChannel) throws Exception {
System.out.println("SESSION STARTED");
}
public void afterSessionEnded(WebSocketSession session, CloseStatus closeStatus, MessageChannel outputChannel) throws Exception {
session.close();
System.out.println("SESSION ENDED");
}
});
return subProtocolWebSocketHandler;
}
}
This works, in the sense that handleMessageFromClient does get triggered on a web socket message, but I fail to understand the purpose of MessageChannel outputChannel and handleMessageToClient.
Is it possible to get the PerConnectionWebSocketHandler semantics with SubProtocolWebSocketHandler?
The documentation around this is basically non-existent e.g. the docs for handleMessageToClient say:
Handle the given {#link Message} to the client associated with the given WebSocket session.
Well, fantastic. And the STOMP implementations are mind-boggling, so they're not very usable as a guideline.
Any example, broad steps or anything, really, would be much appreciated.
Turns out it is exceptionally easy. No need to mess with SubProtocolWebSocketHandler at all. The only requirement is that the provided WebSocketHandler implements SubProtocolCapable.
public class CustomHandler implements WebSocketHandler, SubProtocolCapable {
...
}
That's all. To make a PerConnectionWebSocketHandler, it's enough to simply extend it and implement SubProtocolCapable:
public class CustomHandler extends PerConnectionWebSocketHandler implements SubProtocolCapable {
...
}
After some time I got my WebSocket running with this config:
#Configuration
//#EnableWebSocket
public class WebSocketServerConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/var").setAllowedOrigins("*");
}
#Bean
public WebSocketHandler myHandler() {
return new WebsocketServer();
}
}
But I'm unable to find a way to launch this WebSocket from my code.
Is there a way to launch this WebSocket later in my program?
I found this document but it does not provide a way to implement a startWebSocket() function or something similar.
The document you linked shows an example, where it returns an instance of EchoWebSocketHandler.
You could accept connections and implement your own logic for afterConnectionEstablished or handleMessage using some internal status to determine whether you should accept requests, i.e.
#Configuration
#EnableWebSocket
public class MyConfiguration implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoWebSocketHandler(), "/echo").withSockJS();
}
#Bean
public WebSocketHandler echoWebSocketHandler() {
//FooWebSocketHandler implements your specific logic
return new FooWebSocketHandler();
}
}
public class FooWebSocketHandler extends AbstractWebSocketHandler {
private boolean enabled;
...
public void handleMessage(WebSocketSession session,
WebSocketMessage<?> message)
throws Exception {
if (enabled) {//work
} else {
//disabled, i.e. throw exception or send data according to your api
}
}
}
I am using Spring 4. I use this for execute a task periodically for web sockets:
private TaskScheduler scheduler = new ConcurrentTaskScheduler();
In my class:
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
#Component
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Autowired
private SimpMessagingTemplate template;
private TaskScheduler scheduler = new ConcurrentTaskScheduler();
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/simplemessages").withSockJS();
}
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic/", "/queue/");
config.setApplicationDestinationPrefixes("/app");
}
#PostConstruct
private void broadcastTimePeriodically() {
scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
String statStr = "Server Response" + new Date();
System.out.println("thread schedular run time :" + Hello.printTime());
try {
template.convertAndSend("/topic/simplemessagesresponse", statStr);
} catch (MessagingException e) {
System.err.println("!!!!!! websocket timer error :>" + e.toString());
}
}
}, 4000));
}
#PreDestroy
private void destroyServices() {
scheduler = null; // how to destroy ?
}
public void configureClientInboundChannel(ChannelRegistration registration) {
}
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.taskExecutor().corePoolSize(4).maxPoolSize(10);
}
public boolean configureMessageConverters(List < MessageConverter > arg0) {
// TODO Auto-generated method stub
return true;
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration arg0) {
}
}
I want to know to things:
I found that the scheduler is running twice within 4000 milliseconds. How is it happening and how can I stop it?
I run this application in tomcat. As you can see, the method destroyServices() needs to destroy the schedular. Here the problem is, even the tomcat is restarted again, previously running thread is still running. So when the tomcat is going to down, that thread also should be terminated. I need to know How I can destroy it on tomcat is going to down or any system crash?
The following code snippet is from documentation of #EnableScheduling:
#Configuration
#EnableScheduling
public class AppConfig implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
#Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
}
I think you should get the bean named taskExecutor (in this case) and call shutdown (in fact depending on your configuration) method of it.