I'm writing a bridge application in SpringBoot two bridge an internal messaging protocol over to a websocket based stomp setup. I'm operating in a mixed-mode Kotlin
, java
project (which "might" be making things difficult).
My goal is when I receive a message on our internal messaging queue, i wanted to send it over to the STOMP endpoints.
I can send from STOMP->STOMP
and REST->STOMP
but I seem unable to make the server send directly to a stomp endpoint correclty.
I've gone through just about every single stack post - and I see people have had similar issues, yet, I'm unable to actually get any of the listed solutions working.
From what I gather I need to add:
@Autowired
private SimpMessagingTemplate template;
@Autowired
lateinit var template: SimpleMessagingTemplate
into one of my classes and either add @Controller
or @Service
to the top of the class, possibly making it open
in kotlin
@Controller
public class STOMPDataListener implements SDDFDataListener {
@Autowired
private SimpMessagingTemplate template;
public void handleData(String source, SDDFCommonData data) {
System.out.println("Received: " + data);
}
}
If I add a breakpoint on my handleData
call I find out that:
template = null
@Controller
class BridgeDataListener(val peerID: String = "default") : SDDFDataListener {
@Autowired
lateinit var template: SimpMessagingTemplate
override fun handleData(source: String, data: SDDFCommonData) {
println("Received: $data")
}
}
If I mark it as
@Service
class BridgeDataListener(val peerID: String = "default") : SDDFDataListener {
I get the same error.
I know this is possible because in the Docs: https://docs.spring.io/spring/docs/4.0.1.RELEASE/spring-framework-reference/html/websocket.html they give an example, however, I'm not sure how I could use this example directly because I need to construct multiple versions of my DataListeners - and from what I understand an @Autowired class you get one copy of only? Could I make it a singleton or would that break something?
@Controller
public class GreetingController {
private SimpMessagingTemplate template;
@Autowired
public GreetingController(SimpMessagingTemplate template) {
this.template = template;
}
@RequestMapping(value="/greetings", method=POST)
public void greet(String greeting) {
String text = "[" + getTimestamp() + "]:" + greeting;
this.template.convertAndSend("/topic/greetings", text);
}
}
Assistance is greatly appreciated.
So It looks like I ended up solving my issue.
@Autowired
does not work if you instantiate a class with new
. As I was using some libraries I had to follow approach #3 from https://stackoverflow.com/a/19896871/2069812
So I built a java class:
/**
* See: https://stackoverflow.com/a/19896871/2069812
*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
}
And then in my data handler class I accessed the bean via:
var template: SimpMessagingTemplate = ApplicationContextHolder.getContext().getBean(SimpMessagingTemplate::class.java)
which allowed me to call:
template.convertAndSend("/topic/sddf/$source", json.toString())
template.convertAndSend("/topic/sddf/$source/$peerID", json.toString())