Custom Listeners with detach() method error - vaadin

I would like to know about Vaadin's detach() method. How can I understand below definition from API ?
Called before the UI is removed from the session.
I got a problem when creating custom listener such as BroadCaster .
MyCustomListener.java
public interface MyCustomListener {
void fireEvent(MyCustomEvent event);
}
MyCustomEvent.java
public class MyCustomEvent {
private String message;
public MyCustomEvent(final String message) {
this.message = message;
}
public final String getMessage() {
return message;
}
}
MyCustomDispatcher.java
public final class MyCustomDispatcher {
private static LinkedList<MyCustomListener> customListeners = new LinkedList<MyCustomListener>();
private static ExecutorService executorService = Executors.newSingleThreadExecutor();
private MyCustomDispatcher() {
}
public static synchronized void register(final MyCustomListener listener) {
customListeners.add(listener);
}
public static synchronized void unregister(final MyCustomListener listener) {
customListeners.remove(listener);
}
public static synchronized void invokeMyCustomEvent(final String message) {
if (message == null || message.trim().length() <= 0) {
return;
}
for (final MyCustomListener listener : customListeners) {
executorService.execute(new Runnable() {
public void run() {
listener.fireEvent(new MyCustomEvent(message));
}
});
}
}
}
I call this listener from my UI class as ...
public class HelloWorldUI extends UI implements MyCustomListener {
#Override
protected void init(VaadinRequest request) {
System.out.println("Getting initialized !");
MyCustomDispatcher.register(this);
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
setSizeFull();
layout.addComponent(new Label("Hello World !"));
}
#Override
public void detach() {
System.out.println("Listener was Unregister !");
MyCustomDispatcher.unregister(this);
super.detach();
}
#Override
public void fireEvent(MyCustomEvent event) {
// Do Something
}
}
I call unregister() method of my custom listener inside detach() method for
from some examples for custom listener
to avoid receiving messages for UIs no longer in use (and ensuring that the detached UI can be garbage collected).
Cleaning up resources in a UI
My problem was due to detach() method because when I refreshed my browser , my listener instance was deleted (from detach() method). So , I can't get fireEvent() anymore. I debugged , detach() method was called after init() method of my UI when refreshing browser. But if I remove calling unregister(MyCustomListener listener) from detach() method , that may cause nesting events (previous listeners were still alive).
What am I wrong ? How can I fix it ? Any suggestions ?

Sorry ! this is stupid question . Vaadin's component were server-side codes and I should avoid using static as I much as I can. When I am using my custom listeners as static-resources , these events were share all others. If someone invokes one event , every users will get same.
Static collection of listeners (sharing events) may only suitable for server-push.
I shouldn't create custom listeners as like this.
Thanks #HenriKerola for explanation of using static fields in vaadin and about the creating new UI instance when browser was refresh.

Related

Vaadin 23 override internal error message

when an error occurs inside the application, the user sees the following message:
Is it possible to override it?
I aaded the following:
public class CustomErrorHandler implements ErrorHandler {
private static final Logger logger = LoggerFactory.getLogger(CustomErrorHandler.class);
#Override
public void error(ErrorEvent errorEvent) {
logger.error("Something wrong happened", errorEvent.getThrowable());
Notification.show("An internal error has occurred. Please contact support.");
if (UI.getCurrent() != null) {
UI.getCurrent().access(() -> {
Notification.show("An internal error has occurred. Please contact support.");
});
}
}
}
#Component
public class ServiceListener implements VaadinServiceInitListener {
private static final Logger logger = LoggerFactory.getLogger(LanguageReceiver.class);
#Override
public void serviceInit(ServiceInitEvent event) {
event.getSource().addSessionInitListener(
initEvent -> {
logger.info("A new Session has been initialized!");
VaadinSession.getCurrent().setErrorHandler(new CustomErrorHandler());
});
event.getSource().addUIInitListener(
initEvent -> logger.info("A new UI has been initialized!"));
}
}
#ParentLayout(MainLayout.class)
#AnonymousAllowed
public class ExceptionHandler extends VerticalLayout implements HasErrorParameter<Exception> {
static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class);
#Override
public int setErrorParameter(BeforeEnterEvent event, ErrorParameter<Exception> parameter) {
logger.error("Error", parameter.getException());
Label label = new Label(parameter.getException().getMessage());
add(label);
return HttpServletResponse.SC_NOT_FOUND;
}
}
but still unable to override the mentioned error on the screenshot above. Please show how to do this.
Generally, you need to extend SystemMessages and override getInternalErrorMessage().
Then you can register it using:
YourSystemMessages sysMessages = new YourSystemMessages();
VaadinService.getCurrent().setSystemMessagesProvider(systemMessagesInfo -> sysMessages);
and if you want to reset it to the default one:
VaadinService.getCurrent().setSystemMessagesProvider(DefaultSystemMessagesProvider.get());
In a Spring Boot based application you can register it in any implementation of VaadinServiceInitListener such as:
#Component
public class CustomSystemMessagesInitializer implements VaadinServiceInitListener {
#Autowired
private YourSystemMessages sysMessages;
// You can provide your SystemMessages instance in any way that suits you.
#Override
public void serviceInit(ServiceInitEvent serviceInitEvent) {
serviceInitEvent.getSource()
.setSystemMessagesProvider(systemMessagesInfo -> sysMessages);
}
}
Note that serviceInitEvent.getSource() returns the VaadinService instance, so it can be used as the reference as an alternative to VaadinService.getCurrent.

How can I reroute in BeforeEnterObserver in the view before AppLayout is rendered in Vaadin 14 (Vaadin Flow)

So for example let's I have:
public class MainView extends AppLayout {
public MainView() {
User user = VaadinSession.getAttribute("user");
if(user.isCheckSomething())
Span span = new Span("Hello " + user.getFirstname());
}
}
This will fail with a NPE if the user is not already logged in as getting the User from the session will be null. Now I could add a null check before the if(user.isCheckSomething()) but ideally I would prefer not to have the AppLayout rendered at all if the user is not logged in. That is I'd rather fail in the view through BeforeEnterObserver in the public void beforeEnter(BeforeEnterEvent event) method however the AppLayout is called and created before the beforeEnter(...) method is called.
In other words how can I force the instantiation of AppLayout to be skipped entirely if the user isn't logged in through the view so that the AppLayout is never constructed.
As a rule of thumb, it's really not reliable to work with the UI in the constructor in any way, since it's not initialized yet.
AppLayout has its own afterNavigation method which should be called after BeforeEnterEvent.
public class MainView extends AppLayout {
#Override
protected void afterNavigation() {
super.afterNavigation();
User user = VaadinSession.getAttribute("user");
if(user.isCheckSomething())
Span span = new Span("Hello " + user.getFirstname());
}
So in beforeEnter method you will reroute to login, otherwise the after navigation from the parent layout will be fired.
#Override
public void beforeEnter(BeforeEnterEvent event) {
if (!isAuthenticated()) {
event.rerouteTo(LoginView.class);
}
}
You can either implement BeforeEnterObserver in each of your views and check if user is authenticated over and over again... or you can do it once:
#SpringComponent
public class AuthenticationControl implements VaadinServiceInitListener {
#Override
public void serviceInit(ServiceInitEvent event) {
event.getSource().addUIInitListener(uiEvent -> {
final UI ui = uiEvent.getUI();
ui.addBeforeEnterListener(/* do the check here, either with a method reference or create a separate listener class */);
});
}
}

How to explicitly access the Connector from Widget Side into Vaadin 7?

I create a Widget with his Server Side Class and the Client Side (Connector Class, ServerRPC Class, State Class and Widget Class).
Connector :
#Connect(Custom.class)
public class CustomConnector extends ButtonConnector {
...
public void myFunc() {
// DO Something
}
}
Widget :
public class CustomWidget extends VButton {
...
private CustomConnector conn = new CustomConnector();
public CustomWidget () {
conn.myFunc();
}
...
}
Now from the Widget Class i want to explicitly call/access the Connector Object, which are not a Singleton, so that i can access a function too. How can i solve it?
In my opinion you should not access connector directly from GWT widget. It is against Vaadin 7 architecture where GWT widgets are objects independent from vaadin at all.
However if we are talking about dirty migration from Vaadin 6 to 7 solution could be:
ComponentConnector connector = ConnectorMap.get(client).getConnector(CustomWidget.this); // client is taken from updateFromUIDL method (Vaadin6)
Better solution will be to add "state" listener to the widget
public interface CustomWidgetStateListener {
public void stateChanged();
}
public class CustomWidget extends VButton {
...
CustomWidgetStateListener listener;
public void addStateListener(CustomWidgetStateListener listener) {
this.listener = listener;
}
public void notifyStateChanged() { // You can call notifyStateChanged() whenever you want to notify connector
listener.stateChanged();
}
...
}
public class CustomConnector extends ButtonConnector {
public CustomConnector() {
getWidget().addStateListener(new CustomWidgetStateListener() {
public void stateChanged() {
myFunc();
}
});
}
...
public void myFunc() {
// DO Something
}
}

Hide status message in Blackberry

A status message can be displayed in Blackberry using method show of class net.rim.device.api.ui.component.Status. Using this method you specify a certain amount of time that the message should be displayed but, is there any way to hide this status message BEFORE this time?
I am using this code for displaying status messages in my application:
public static void status(final String message, final int time) {
UiApplication.getUiApplication().invokeLater(new Runnable() { public void run() { Status.show(message, time); } });
}
EDIT: Solution that worked for me (thanks to Eugen Martynov solution)
public static void hideStatus() {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Screen activeScreen = UiApplication.getUiApplication().getActiveScreen();
if (activeScreen instanceof Status) {
activeScreen.close();
}
}
});
}
According to javadoc there no concrete method to close status.
However you could try to ask UiApplication.getUiApplication().getActiveScreen() and try to call close() of it

Message routing/handling with custom annotations and CDI injection

I have a Java EE 6 web application and use the WebSocket protocol to communicate with browsers. The browser can send various types of messages and in the servers onMessage method I would like to route (or dispatch) the message to a specific message handler class depending on the message type. I would like to configure or register these message handlers via annotations, similar to the mechanism of servlets (#WebServlet("/there")). And like in servlets, I would like to be able to use CDI injection in the message handlers.
For now I have a MessageType annotation, a MessageHandler interface and 3 implementations.
#Documented
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface MessageType
{
String value();
}
public interface MessageHandler
{
public void processMessage(String inputMesssage);
}
#MessageType("first")
public class FirstMessageHandler implements MessageHandler
{
#Inject
ResourceBundleProvider resourceBundleProvider;
#Override
public void processMessage(String inputMesssage)
{
System.out.println("FirstMessageHandler#processMessage: " + inputMesssage);
System.out.println("InjectionTest: " + resourceBundleProvider.getValue("label.language"));
}
}
#MessageType("second")
public class SecondMessageHandler implements MessageHandler
{
#Override
public void processMessage(String inputMesssage)
{
System.out.println("SecondMessageHandler#processMessage: " + inputMesssage);
}
}
public class DefaultMessageHandler implements MessageHandler
{
#Override
public void processMessage(String inputMesssage)
{
System.out.println("DefaultMessageHandler#processMessage: " + inputMesssage);
}
}
I also have a class MessageDispatcher which uses reflections to scan the classpath for the annotated message handlers, instantiates them and puts them into a map:
#ApplicationScoped
public class MessageDispatcher
{
private Map<String, MessageHandler> messageHandlerMap = new HashMap<String, MessageHandler>();
#Inject
DefaultMessageHandler defaultMessageHandler;
public MessageDispatcher()
{
registerAnnotatedHandlers();
}
private void registerAnnotatedHandlers()
{
Reflections reflections = new Reflections("namespace");
try
{
for (Class<?> annotatedClass : reflections.getTypesAnnotatedWith(MessageType.class))
{
String annotationValue = annotatedClass.getAnnotation(MessageType.class).value();
for (Class<?> interfaceClass : annotatedClass.getInterfaces())
if (!annotationValue.isEmpty() && interfaceClass.equals(MessageHandler.class))
messageHandlerMap.put(annotationValue, (MessageHandler) annotatedClass.newInstance());
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
public MessageHandler getMessageHandler(String key)
{
MessageHandler messageHandler = messageHandlerMap.get(key);
return messageHandler != null ? messageHandler : defaultMessageHandler;
}
}
And finally in my websocket servlet's onMessage method I extract the key from the inbound message and use it for the message routing:
public synchronized void onMessage(String data)
{
String[] message = data.split(":");
// Choose the message handler from the message
MessageHandler messageHandler = messageDispatcher.getMessageHandler(message[0]);
// Process the message by the message handler
messageHandler.processMessage(message[1]);
}
My 3 incoming sample messages are:
"first:Message to handle with FirstMessageHandler"
"second:Message to handle with SecondMessageHandler"
"third:Message to handle with DefaultMessageHandler"
This works fine, The first and second messages are processed by FirstMessageHandler and SecondMessageHandler respectively. The third message is processed by the default message handler since there is no other handler registered for handling the key "third".
My Problem: I cannot use injection in the message handlers because they are created using Java reflection. Does anybody know how to get annotation processing and CDI injection 'married'? Or does anybody think this approach is bullshit and has another solution for that?
Best Regards
Sebastian
This is my final approach:
I spend a PostConstruct method to my MessageDispachter where I look for all message handler beans. For each of these beans I get their annotation value and a reference to the bean (which also includes creation of the bean). Then I store both, the annotation value and the bean reference into my messageHandlerMap. There is a lot of CDI delegating and interception involved, but it works:
public class MessageDispatcher
{
private Map<String, MessageHandler> messageHandlerMap = new HashMap<String, MessageHandler>();
#Inject
DefaultMessageHandler defaultMessageHandler;
#Inject
BeanManager beanManager;
#PostConstruct
public void registerHandlers()
{
Set<Bean<?>> messageHandlerBeans = beanManager.getBeans(MessageHandler.class, new MessageTypeLiteral());
for (Bean<?> bean : messageHandlerBeans)
{
String key = bean.getBeanClass().getAnnotation(MessageType.class).value();
if (!key.isEmpty())
{
CreationalContext<?> creationalContext = beanManager.createCreationalContext(bean);
MessageHandler messageHandler = (MessageHandler) beanManager.getReference(bean, MessageHandler.class, creationalContext);
messageHandlerMap.put(key, messageHandler);
}
}
}
public MessageHandler getMessageHandler(String key)
{
MessageHandler messageHandler = (MessageHandler) messageHandlerMap.get(key);
return messageHandler != null ? messageHandler : defaultMessageHandler;
}
}
#Documented
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface MessageType
{
#Nonbinding
String value();
}
#SuppressWarnings("all")
public class MessageTypeLiteral extends AnnotationLiteral<MessageType> implements MessageType
{
private static final long serialVersionUID = 1L;
#Override
public String value()
{
return "";
}
}
public class DefaultMessageHandler implements MessageHandler
{
#Inject
ResourceBundleProvider resourceBundleProvider;
#Override
public void processMessage(String inputMesssage)
{
...
#MessageType("first")
public class FirstMessageHandler implements MessageHandler
{
#Inject
ResourceBundleProvider resourceBundleProvider;
#Override
public void processMessage(String inputMesssage)
{
...
The #NonBinding annotation in the #MessageType annotation seems to be important to find all beans annotated with #MessageType("xxx") independent of the actual annotation value (here: xxx).
I hope this explains the important things. For further details please ask me
Sebastian
I think your simplest solution to this would be to keep what you have, strip out the scanning because you don't need it, change your annotation to be a qualifier and fire a CDI event with the qualifier (you'll need to create an AnnotationLiteral for each of three different qualifiers because the value is binding) and the message as the payload.
I can explain more if you need it.
See and adjust Dynamically fire CDI event with qualifier with members
It is a CDI way for dynamic runtime selecting services by runtime decision. The TypeEnum can also be a String.

Resources