Spring Websocket MessageSendingOperation not working without #Scheduled - java

.
.
.
#Autowired
private MessageSendingOperations<String> messagingTemplate;
#Scheduled(fixedDelay = 5000)
public void sendMessage(){
messagingTemplate.convertAndSend("/topic/data","hello");
}
}
The above works. but
sendMessage();
public void sendMessage(){
messagingTemplate.convertAndSend("/topic/data", "hello");
}
}
doesn't work, it got java.lang.NullPointerException
Does that mean I could only use messagingTemplate.convertAndSend(...) inside a function annotated with #Scheduled?
Please help...
#Deendayal Garg, thanks for your help, I did this this according to your advice:
public class Communicator implements ApplicationListener {
private final MessageSendingOperations<String> messagingTemplate;
#Autowired
public Communicator(final MessageSendingOperations<String> messagingTemplate){
this.messagingTemplate = messagingTemplate;
}
public void onApplicationEvent(BrokerAvailabilityEvent event) {
// TODO Auto-generated method stub
}
public void sendMessage(Message data) {
this.messagingTemplate.convertAndSend("/topic/data",data.getBody());
System.out.println(new Random().nextInt(100));
}
}
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(
final DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public Communicator communicator(){
Communicator comm = new Communicator();
return comm;
}
}
Then I injected the bean in the class below
public class StepExecListener implements StepExecutionListener, ApplicationListener<BrokerAvailabilityEvent>{
#Autowired
Communicator communicator;
Message msg = new Message();
msg.setBody("Twale!");
msg.setType("test");
communicator.sendDataUpdates(msg);
}
But I still got it got java.lang.NullPointerException after calling communicator.sendDataUpdates(msg);

Related

Dependency Injection with interface in Spring

I've a MainHandler class :
#Component
class MainHandler {
//inject this
private Handler handler;
#Autowired
public MainHandler(Handler handler){
this.handler = handler;
}
public void action(String message){
//watch photo
if (message.equals("photo")){
handler.handle();
}
if(message.equals("audio")){
//play music
handler.handle();
}
if(message.equals("video")){
//play video
handler.handle();
}
}
And following other handlers with interface.
Can I inject dependencies with Spring Boot by only interface type handler?
#Component
public interface Handler {
void handle();
}
#Component
class PhotoHandler implements Handler {
public void handle(){
System.out.println("Featuring photo...");
}
}
#Component
class VideoHandler implements Handler {
public void handle(){
System.out.println("Playing video...");
}
}
#Component
class AudioHandler implements Handler {
public void handle(){
System.out.println("Playing music...");
}
}
Or I want to try something like this below. Is it possible ?
class MainHandler {
private VideoHandler videoHandler;
private AudioHandler audioHandler;
private PhotoHandler photoHandler;
#Autowired
public MainHandler(VideoHandler videoHandler,
AudioHandler audioHandler,
PhotoHandler photoHandler) {
this.videoHandler = videoHandler;
this.audioHandler = audioHandler;
this.photoHandler = photoHandler;
}
public void action(String message){
//watch photo
if (message.equals("photo")){
photoHandler.handle();
}
if(message.equals("audio")){
//play music
audioHandler.handle();
}
if(message.equals("video")){
//play video
videoHandler.handle();
}
}
}
So, type of handler depends on user's message. I don't know how Spring can choose which handler gonna be used in this context. Any solution?
There can be multiple solution to this case.
Option #1
You can tweak a design of your handler a bit.
For instance you can introduce a method
boolean canHandle(String message);
so each handler can answer whether passed message can be handled or not.
Then you can inject a list of all handlers into your MainHandler.
private List<Handler> handlers;
Now having that list you can call each handler by message:
public void action(String message) {
handlers.stream()
.filter(h -> h.canHandle(message))
.forEach(handler -> handler.handle());
}
Full example:
#SpringBootApplication
public class SO62370917 {
public static void main(String[] args) {
SpringApplication.run(SO62370917.class, args);
}
#Component
static class MainHandler {
private final List<Handler> handlers;
MainHandler(List<Handler> handlers) {
this.handlers = handlers;
}
public void action(String message) {
handlers.stream()
.filter(h -> h.canHandle(message))
.forEach(Handler::handle);
}
}
#Bean
CommandLineRunner cmd(MainHandler mainHandler) {
return args -> {
mainHandler.action("video");
mainHandler.action("audio");
mainHandler.action("photo");
};
}
interface Handler {
void handle();
boolean canHandle(String message);
}
#Component
class PhotoHandler implements Handler {
public void handle(){
System.out.println("Featuring photo...");
}
#Override
public boolean canHandle(String message) {
return "photo".equals(message);
}
}
#Component
class VideoHandler implements Handler {
public void handle(){
System.out.println("Playing video...");
}
#Override
public boolean canHandle(String message) {
return "video".equals(message);
}
}
#Component
class AudioHandler implements Handler {
public void handle(){
System.out.println("Playing music...");
}
#Override
public boolean canHandle(String message) {
return "audio".equals(message);
}
}
}
Option #2
Use qualifiers.
You can name your handlers however you like and then inject a Map<String, Handler> into your mainHandler. The key would be a bean name and the value - the actual handler. Spring will automatically take care of this.
#SpringBootApplication
public class SO62370917 {
public static void main(String[] args) {
SpringApplication.run(SO62370917.class, args);
}
#Component
static class MainHandler {
private final Map<String, Handler> handlers;
MainHandler(Map<String, Handler> handlers) {
this.handlers = handlers;
}
public void action(String message) {
if (handlers.containsKey(message)) {
handlers.get(message).handle();
}
}
}
#Bean
CommandLineRunner cmd(MainHandler mainHandler) {
return args -> {
mainHandler.action("video");
mainHandler.action("audio");
mainHandler.action("photo");
};
}
interface Handler {
void handle();
}
#Component("photo")
class PhotoHandler implements Handler {
public void handle() {
System.out.println("Featuring photo...");
}
}
#Component("video")
class VideoHandler implements Handler {
public void handle() {
System.out.println("Playing video...");
}
}
#Component("audio")
class AudioHandler implements Handler {
public void handle() {
System.out.println("Playing music...");
}
}
}
The output:
2020-06-14 13:06:47.140 INFO 29447 --- [ main] com.example.demo.SO62370917 : Started SO62370917 in 1.356 seconds (JVM running for 1.795)
Playing video...
Playing music...
Featuring photo...
There are two simple ways in which you can approach :
Recommended : You can use #Qualifier to inject the desired particular bean.
For example
#Component
class MainHandler {
#Autowired
#Qualifier("videoHandler") // example
private Handler handler;
public void action(){
handler.message(); // this will print playing video...
}
}
You can inject the ApplicationContext.
For example :
#Component
class MainHandler {
#Autowired
private ApplicationContext context;
public void action(String message){
//watch photo
if (message.equals("photo")){
((PhotoHandler) context.getBean(PhotoHandler.class)).handle();
}
if(message.equals("audio")){
//play music
((AudioHandler) context.getBean(AudioHandler.class)).handle();
}
if(message.equals("video")){
//play video
((VideoHandler) context.getBean(VideoHandler.class)).handle();
}
}
}

Android testing - Robolectric + Mockito + Retrofit getting Wanted but not invoked error

Trying to use Robolectric and Mockito to test my Retrofit calls in my Android app but I am getting the following error:
Wanted but not invoked: mockApi.register(
,
);
-> at ServiceTest.testAPI(ServiceTest.java:58) Actually, there were zero interactions with this mock.
The RetroFit API call is defined in an interface as follows:
#FormUrlEncoded
#POST("/register")
void register(
#FieldMap Map<String, String> registrationParams,
Callback<JsonObject> response) ;
My test class is as follows:
#Config(constants = BuildConfig.class)
#RunWith(TestRunner.class)
public class SharedServiceTest {
private RegistrationActivity activity;
#Mock
private SharedService mockApi;
#Captor
private ArgumentCaptor<Callback<JsonObject>> cb;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ActivityController<RegistrationActivity> controller = Robolectric.buildActivity(RegistrationActivity.class);
activity = controller.get();
controller.create();
}
#Test
public void testAPI() throws Exception {
activity.populateFields();
activity.validateFields();
activity.register("");
Mockito.verify(mockApi).register(Mockito.anyMap(), cb.capture());
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("sessionToken", Mockito.anyString());
jsonObject.addProperty("userId", Mockito.anyString());
cb.getValue().success(jsonObject, null);
Assert.assertTrue(ShadowToast.getTextOfLatestToast().contains("Registration completed"));
}
}
The method in my RegistrationActivity that uses the API is as follows:
public void register(){
MyApplication.getInstance().getSharedService().register(mRegistrationParams, new Callback<JsonObject>() {
#Override
public void success(JsonObject jsonObject, retrofit.client.Response response) {
Toast.makeText(RegistrationActivity.this, "Registration completed", Toast.LENGTH_LONG).show();
}
#Override
public void failure(RetrofitError error) {
Toast.makeText(RegistrationActivity.this, RetrofitUtils.getErrorMessage(error), Toast.LENGTH_LONG).show();
}
});
}
The real Retrofit service comes from my own Application class which I have mocked in my test folder for robolectric to use:
public class TestMyApplication extends MyApplication
implements TestLifecycleApplication {
#Override
public void onCreate() {
super.onCreate();
}
#Override public void beforeTest(Method method) {
}
#Override public void prepareTest(Object test) {
}
#Override public void afterTest(Method method) {
}
#Override public CPSharedService getCPSharedService() {
return Mockito.mock(SharedService.class);
}
}
I have searched over the other questions on SO that have this error but none of them match what I am trying to do here or provide a solution to my issue so I am just wondering what I am doing wrong?
The mocked instance of SharedService in your TestMyApplication is not the same you declared your test class.
The Mockito.verify(mockApi).register(Mockito.anyMap(), cb.capture()); is failing because the instance referred by mockApi field is actually never called.
Another problem is that the getter in TestMyApplication always returns a new mock for each invokation:
#Override public CPSharedService getCPSharedService() {
return Mockito.mock(SharedService.class); //this creates a new "mocked" instance
}
Your scenario is not 100% clear to me, but it would be better if you could let your test set the instance of the mockApi field in your TestMyApplication instance:
public class TestMyApplication extends MyApplication
implements TestLifecycleApplication {
private SharedService sharedService;
#Override
public void onCreate() {
super.onCreate();
}
#Override public void beforeTest(Method method) {
}
#Override public void prepareTest(Object test) {
}
#Override public void afterTest(Method method) {
}
#Override public CPSharedService getCPSharedService() {
return this.sharedService;
}
public void setCPSharedService(SharedService sharedService) {
// store your mock
this.sharedService = sharedService;
}
}
and in your test class:
#Test
public void testAPI() throws Exception {
// configure you TestMyApplication
assertTrue(MyApplication.getInstance() instanceof TestMyApplication);
TestMyApplication testMyApp = (TestMyApplication) MyApplication.getInstance();
testMyApp.setCPSharedService(this.mockApi);
activity.populateFields();
activity.validateFields();
activity.register("");
Mockito.verify(this.mockApi).register(Mockito.anyMap(), cb.capture());
...
}

Vaadin+Spring (without SpringBoot) with JavaConfig

i'm trying to combine vaadin with spring (without spring-boot) and java-annotation based configuration for the spring part.
The autowiring seems to work on the vaadin-ui part but not in "custom-ui classes" (e.g. "public class LoginScreen extends CustomComponent"). I'm getting an NPE or a null-object on SysOut.
Further i noticed that "#ComponentScan(basePackages={"net.myapp"})" is not scanning for beans. The only way to declare beans is in the CustomConfiguration itself.
XML-Configuration is not something i prefer.
I'm following this Tutorial: Link
CustomConfiguration.java
#Configuration
#ComponentScan(basePackages={"net.myapp"})
#EnableVaadin
public class CustomConfiguration {
// this is working but i want to use componentscan!
#Bean
public String test() {
return "test...";
}
#Bean
public TestBean testBean() {
return new TestBean();
}
#Bean
public LoginScreen loginScreenBean() {
return new LoginScreen();
}
}
SpringVaadinServlet.java
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = Application.class)
#SuppressWarnings("serial")
public class SpringVaadinServlet extends VaadinServlet implements SessionInitListener {
#Autowired
protected VaadinUIProvider applicationProvider;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
AutowireCapableBeanFactory ctx = ((ApplicationContext)
getServletContext().getAttribute("applicationContext")).getAutowireCapableBeanFactory();
ctx.autowireBean(this);
}
#Override
protected void servletInitialized() {
getService().addSessionInitListener(this);
}
#Override
public void sessionInit(SessionInitEvent event) throws ServiceException {
event.getSession().addUIProvider(applicationProvider);
}
}
VaadinUIProvider.java
#SpringComponent
#SuppressWarnings("serial")
public class VaadinUIProvider extends UIProvider {
#Autowired
ApplicationContext applicationContext;
#Override
public Class<? extends UI> getUIClass(UIClassSelectionEvent event) {
return Application.class;
}
#Override
public UI createInstance(UICreateEvent event) {
UI instance = new Application();
System.out.println("applicationContext is null? " + applicationContext);
applicationContext.getAutowireCapableBeanFactory().autowireBean(instance);
return instance;
}
}
SpringApplicationContextListener.java
#WebListener
public class SpringApplicationContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CustomConfiguration.class);
sce.getServletContext().setAttribute("applicationContext", applicationContext);
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
Application.java
#Theme("mytheme1")
#SpringUI
#SuppressWarnings("serial")
public class Application extends UI {
#Autowired
private TestBean testBean;
#Autowired
private String test;
#Override
protected void init(VaadinRequest vaadinRequest) {
// working
System.out.println("init testBean: " + testBean);
System.out.println("init test: " + test);
Window window = new Window();
window.setContent(new LoginScreen());
window.setClosable(false);
window.setWidth("400px");
window.setHeight("280px");
window.setModal(true);
window.setDraggable(false);
window.setResizable(false);
window.center();
addWindow(window);
setSizeFull();
}
}
And the following "custom-ui class"
LoginScreen.java
#UIScope
#SuppressWarnings("serial")
public class LoginScreen extends CustomComponent {
public static final String VIEW_NAME = "";
final FormLayout layout = new FormLayout();
TextField userName = new TextField();
TextField passWord = new TextField();
Button submit = new Button("Submit");
#Autowired
private TestBean testBean;
#Autowired
private String test;
public LoginScreen() {
userName.setCaption("Benutzername:");
passWord.setCaption("Passwort:");
// not working (null)
System.out.println("loginscreen test: " + testBean);
System.out.println("loginscreen test: " + test);
setSizeFull();
}
}
I'd appreciate some help...
window.setContent(new LoginScreen());
Spring should create LoginScreen if you want that #Autowired annotated fields become injected.
Just inject the LoginScreen instance in your Application class

Spring #Async method inside a Service

I have this service bean with a sync method calling the internal async method:
#Service
public class MyService {
public worker() {
asyncJob();
}
#Async
void asyncJob() {
...
}
}
The trouble is that the asyncJob is not really called in async way.
I found that this doesn't work because an internal call skips the AOP proxy.
So I try to self-refer the bean:
#Service
public class MyService {
MyService mySelf;
#Autowired
ApplicationContext cnt;
#PostConstruct
public void init() {
mySelf=(MyService)cnt.getBean("myService");
}
public void worker() {
mySelf.asyncJob();
}
#Async
void asyncJob() {
...
}
}
It fails. Again no async call.
So I tried to divide it in two beans:
#Service
public class MyService {
#Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
void asyncJob() {
...
}
}
Fails again.
The only working way is to call it from a Controller Bean:
#Controller
public class MyController {
#Autowired
MyAsyncService myAsyncService;
#RequestMapping("/test")
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
public void asyncJob() {
...
}
}
But in this case it is a service job. Why I cannot call it from a service?
Found a really nice way to solve this (with java8) in the case where you have a lot of various things you want to both sync and async. Instead of creating a separate XXXAsync service for each 'synchronous' service, create a generic async service wrapper:
#Service
public class AsyncService {
#Async
public void run(final Runnable runnable) {
runnable.run();
}
}
and then use it as such:
#Service
public class MyService {
#Autowired
private AsyncService asyncService;
public void refreshAsync() {
asyncService.run(this::refresh);
}
public void refresh() {
// my business logic
}
public void refreshWithParamsAsync(String param1, Integer param2) {
asyncService.run(() -> this.refreshWithParams(param1, param2));
}
public void refreshWithParams(String param1, Integer param2) {
// my business logic with parameters
}
}
I solved the third method (divide it in two beans) changing the async method's access modifier to public:
#Service
public class MyService {
#Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
public void asyncJob() { // switched to public
...
}
}
In my case, it was easier to remove the #Async annotation and use the taskExecutor directly to submit my task:
Before
#Async("taskExecutor")
private Future<U> executerEnAsync(
final T pInput) {
final U resultat = this.appelerBS(pInput);
return new AsyncResult<U>(resultat);
}
After
#Autowired
private AsyncTaskExecutor taskExecutor;
private Future<U> executerEnAsync(
final T pInput) {
final Future<U> future = taskExecutor.submit(new Callable<U>() {
#Override
public U call() {
final U resultat = appelerBS(pInput);
return resultat;
}
});
return future;
}

Spring websocket - Activemq listener integration

I am using spring 4.0.0.rc1 websocket endpoint and topic.
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
#ComponentScan(basePackages="org.springframework.samples")
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocketendpoint").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerConfigurer configurer) {
configurer.enableSimpleBroker("/queue/", "/topic/");
}
}
But JMS listener can not post a message on websocket-topic. In this case I am using camel-consume annotation to listen to queue and post message to topic.
#Service
public class WebsocketTopicService implements ApplicationListener<BrokerAvailabilityEvent>
{
private static final Logger log = Logger.getLogger(MessageChannelService.class);
private final MessageSendingOperations<String> messagingTemplate;
private AtomicBoolean brokerAvailable = new AtomicBoolean();
#Autowired
public MessageChannelService(MessageSendingOperations<String> messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#Override
public void onApplicationEvent(BrokerAvailabilityEvent event) {
this.brokerAvailable.set(event.isBrokerAvailable());
}
//#Scheduled(fixedDelay=2000)
#Consume(uri="activemq:queue.out")
public void processMessage(String msg){
if (this.brokerAvailable.get()) {
this.messagingTemplate.convertAndSend("/topic/message.status", msg);
log.info("Sending quote " + msg);
}
}
Any idea why ??

Categories