I created an asynchronous thread to navigate from one UI class to another UI class after 30 seconds by showing a timer(H1 tag) to the user. Thread successfully shows updates on H1 tag but does not navigate to the next UI class after the end of 30 seconds. I'm getting an error Exception call "java.lang.IllegalStateException: Cannot access state in VaadinSession or UI without locking the session." for ui.navigate(ScoreBoard.class); call.
#Override
protected void onAttach(AttachEvent attachEvent) {
// Start the data feed thread
thread = new FeederThread(attachEvent.getUI(),timerc);
thread.start();
}
//Thread
private static class FeederThread extends Thread {
private final com.vaadin.flow.component.UI ui;
private final H1 element;
private int count = 30;
public FeederThread(com.vaadin.flow.component.UI ui,H1 element) {
this.ui = ui;
this.element = element;
}
#Override
public void run() {
while (count>-1){
try {
Thread.sleep(1000);
ui.access(()-> {
element.setText(String.valueOf(count)+" sec");
});
count--;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//Exception in thread "Thread-46" java.lang.IllegalStateException: //Cannot access state in VaadinSession or UI without locking the session.
ui.navigate(ScoreBoard.class);
}
}
//Exception in thread "Thread-46" java.lang.IllegalStateException: //Cannot access state in VaadinSession or UI without locking the session.
ui.navigate(ScoreBoard.class);
UI.getCurrent() is returning null when called in Thread, this is intentional. This way it can be ensured that no wrong UI is returned.
The correct pattern would be for example add a method in your view, which updates the Text. In the method you can use getUi().ifPresent(ui -> ui.access(..)) . Then you can call that method from the Thread safely. Same can be applied with navigation.
Alternatively you can pass ui as parameter to your Thread as you have done. When you do so, getCurrent() call is obsolote.
You need to enable push by using #Push in your class. Also, since the navigation action is part of the UI state, you need to use UI.access. Finally, you don't need to call getCurrent() if you already have the instance. So this is what you need in short:
...
#Push
public class MainView extends VerticalLayout {
...
ui.access(() -> ui.navigate(ScoreBoard.class));
...
}
Related
When a DataProvider fetch or count method throws an exception, e.g. because the user is not authorized, how could I handle these exceptions centrally? I know there is HasErrorParameter interface to show error views when there is an exception thrown when routing. But these error views are not triggered when DataProvider throws the exception.
Example:
new AbstractBackEndDataProvider<String, Void>() {
#Override
protected Stream<String> fetchFromBackEnd(Query<String, Void> query) {
...
}
#Override
protected int sizeInBackEnd(Query<String, Void> query) {
throw new UnsupportedOperationException("test");
}
}
#Route("failed")
public class FailView extends VerticalLayout
implements HasErrorParameter<UnsupportedOperationException> {...}
Even if I do a try catch within the DataProvider methods, I don't see how I could navigate to the appropriate error view just by using the caught exception and not the view component class (this wouldn't trigger setErrorParameter method).
BTW: I miss the router exception handling topic in Vaadin Flow 13 documentation. I wonder why they removed it.
I believe all Exceptions that don't occur while routing will be given to the ErrorHandler of the VaadinSession the error occured in.
The best way to set the ErrorHandler seems to be to override the sessionInit method in a custom SessionInitListener
You can add a custom SessionInitListener inside the servletInitialized method of a custom VaadinServlet.
class CustomServlet extends VaadinServlet{
#Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
getService().addSessionInitListener(new CustomSessionInitListener());
}
}
And that SessionInitListener (in this example CustomSessionInitListener) has to set the errorHandler of the sessions that get initialized.
class CustomSessionInitListener implements SessionInitListener{
#Override
public void sessionInit(SessionInitEvent event) throws ServiceException {
event.getSession().setErrorHandler(new CustomErrorHandler());
}
}
For further information on how to create your own Servlet take a look at Vaadin's tutorial page(you need to scroll down to "Customizing Vaadin Servlet")
Edit:
To show the error page you need to get Vaadin to reroute to an error. To achieve that we can use an BeforeEnterEvent, BeforeEnterEvents have a rerouteToError method which we can use to let Vaadin show our ErrorView.
But we also want to pass along the Exception instance, so we have to store that as well. I did exactly that with the following class:
#Route("error-view") // Route shown in the user's browser
public class ErrorViewShower extends Div implements BeforeEnterObserver {
// Class to store the current Exception of each UI in
private static class UIExceptionContainer extends HashMap<UI, Exception> {
}
// Method to call when we want to show an error
public static void showError(Exception exception) {
UIExceptionContainer exceptionContainer = VaadinSession.getCurrent().getAttribute(UIExceptionContainer.class);
// Creating and setting the exceptionContainer in case it hasn't been set yet.
if (exceptionContainer == null) {
exceptionContainer = new UIExceptionContainer();
VaadinSession.getCurrent().setAttribute(UIExceptionContainer.class, exceptionContainer);
}
// Storing the exception for the beforeEnter method
exceptionContainer.put(UI.getCurrent(), exception);
// Now we navigate to an Instance of this class, to use the BeforeEnterEvent to reroute to the actual error view
UI.getCurrent().navigate(ErrorViewShower.class);// If this call doesn't work you might want to wrap into UI.access
}
#Override
public void beforeEnter(BeforeEnterEvent event) {
UIExceptionContainer exceptionContainer = VaadinSession.getCurrent().getAttribute(UIExceptionContainer.class);
// Retrieving the previously stored exception. You might want to handle if this has been called without setting any Exception.
Exception exception = exceptionContainer.get(UI.getCurrent());
//Clearing out the now handled Exception
exceptionContainer.remove(UI.getCurrent());
// Using the BeforeEnterEvent to show the error
event.rerouteToError(exception, "Possible custom message for the ErrorHandler here");
}
}
Usage of it in combination with the error handler looks like this:
public class CustomErrorHandler implements ErrorHandler {
#Override
public void error(ErrorEvent event) {
// This can easily throw an exception itself, you need to add additional checking before casting.
// And it's possible that this method is called outside the context of an UI(when a dynamic resource throws an exception for example)
Exception exception = (Exception) event.getThrowable();
ErrorViewShower.showError(exception);
}
}
Edit2:
As it turns out that Exceptions occuring inside internal method calls don't get handled by the UI's ErrorHandler or the VaadinSession's ErrorHandler but instead by another error handler which causes the client side to terminate and show the Error Notification,
a solution is to catch the Exceptions inside the methods of the DataProvider and pass them to ErrorViewShower.showError() and still return without any Exception flying the stacktrace upwards. (Or don't throw any Exception yourself and instead simply pass a new to the ErrorViewShower.showError() method).
By returning normally Vaadin doesn't even know something went wrong.
ErrorViewShower.showError() calls ui.navigate, that navigation command seems to get "queued" behind the calls to the DataProvider, meaning the view of the user will change in the same request.
Dataprovider with such an implementation:
new AbstractBackEndDataProvider<String, Void>() {
#Override
protected Stream<String> fetchFromBackEnd(Query<String, Void> query) {
try{
//Code that can throw an Exception here
}catch(Exception e){
ErrorViewShower.showError(e);
//We have to make sure that query.getLimit and query.getOffset gets called, otherwise Vaadin throws an Exception with the message "the data provider hasn't ever called getLimit() method on the provided query. It means that the the data provider breaks the contract and the returned stream contains unxpected data."
query.getLimit();
query.getOffset();
return Stream.of(); //Stream of empty Array to return without error
}
}
#Override
protected int sizeInBackEnd(Query<String, Void> query) {
//Second way i mentioned, but this will not catch any Exception you didn't create, where as the try...catch has no way to let any Exception reach Vaadin.
if(badThingsHappened){
ErrorViewShower.showError(new UnsupportedOperationException("Bad things..."));
return 0;//Exiting without error
}
}
}
MainView include InformationCOmponent:
#Push
#Route
public class MainView extends VerticalLayout {
InformationComponent infoComponent;
public MainView(#Autowired StudentRepository studentRepo, #Autowired Job jobImportCsv, #Autowired JobLauncher jobLauncher, #Value("${file.local-tmp-file}") String inputFile) {
[...] // some stuffs
infoComponent = new InformationComponent(studentRepo);
add(infoComponent);
}
//update when job process is over
private void uploadFileSuccceed() {
infoComponent.update(myUploadComponent.getFile());
}
InformationComponent:
public class InformationComponent extends HorizontalLayout {
StudentRepository studentRepo;
Label nbLineInFile = new Label();
VerticalLayout componentLeft = new VerticalLayout();;
VerticalLayout componentRight = new VerticalLayout();;
public InformationComponent(StudentRepository studentRepo) {
[...] // some init and style stuff
addLine("Nombre de lignes dans le fichier", nbLineInFile);
}
private void addLine(String label, Label value) {
componentLeft.add(new Label(label));
componentRight.add(value);
}
public void update(File file) {
try {
long nbLines = Files.lines(file.toPath(), Charset.defaultCharset()).count();
System.out.println("UPDATED! " +nbLines); // value is display in console ok!
UI.getCurrent().access(() -> nbLineInFile.setText(nbLines)); // UI is not updated!!
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
When I call InformationComponent from MainView the Label is not update in the browser.
UI.getCurrent().access(() -> nbLineInFile.setText(nbLines))
also try wwith #Push(PushMode.MANUAL) and ui.push(); but doesn't work either...
Complete source code is here: https://github.com/Tyvain/ProcessUploadedFile-Vaadin_SpringBatch/tree/push-not-working
I suspect the problem here is that uploadFileSuccceed() is run from a background thread, in which case UI.getCurrent() will return null. This would cause a NullPointerException that either kills the background thread or alternatively the exception is caught and silently ignored by the caller. Another alternative is that uploadFileSuccceed() happens through a different browser window and thus also a different UI instance, which means that the changes would be pushed in the context of the wrong UI.
For exactly these reasons, UI.getCurrent().access(...) is generally an anti pattern, even though it's unfortunately quite widely used in old examples.
You can check whether this is the cause of your problem by logging the value of UI.getCurrent() in the beginning of the update method, and comparing that to the value of UI.getCurrent() e.g. in the constructor of InformationComponent.
To properly fix the problem, you should pass the correct UI instance through the entire chain of events originating from whatever triggers the background processing to start. You should also note that it might be tempting to use the getUI() method that is available in any Component subclass, but that method is not thread safe and should thus be avoided in background threads.
As a final notice, I would recommend using the Span or Text component instead of Label in cases like this. In Vaadin 10, the Label component has been changed to use the <label> HTML element, which means that it's mainly intended to be used as the label of an input component.
Based on information provided by Leif you should do something like the following example.
At runtime, when this HorizontalLayout subclass object is attached to a parent UI object, its onAttach method is called. At that point we can remember the UI by storing its reference is a member variable named ui. Actually, an Optional<UI> is returned rather than a UI object, so we need to test for null, though it should never be null at point of onAttach.
public class InformationComponent extends HorizontalLayout {
UI ui;
StudentRepository studentRepo;
Label nbLineInFile = new Label();
VerticalLayout componentLeft = new VerticalLayout();;
VerticalLayout componentRight = new VerticalLayout();;
public InformationComponent(StudentRepository studentRepo) {
[...] // some init and style stuff
addLine("Nombre de lignes dans le fichier", nbLineInFile);
}
private void addLine(String label, Label value) {
componentLeft.add(new Label(label));
componentRight.add(value);
}
public void update(File file) {
try {
long nbLines = Files.lines(file.toPath(), Charset.defaultCharset()).count();
System.out.println("UPDATED! " +nbLines); // value is display in console ok!
this.ui.access(() -> nbLineInFile.setText(nbLines)); // UI is not updated!!
} catch (IOException e) {
throw new RuntimeException(e);
} catch (UIDetachedException e) {
// Do here what is needed to do if UI is no longer attached, user has closed the browser
}
#Override // Called when this component (this `HorizontalLayout`) is attached to a `UI` object.
public void onAttach() {
ui = this.getUI().orElseThrow( () -> new IllegalStateException("No UI found, which should be impossible at point of `onAttach` being called.") );
}
I have an application that does not recieve ordinary HTTP requests through a controller, instead it listens to and receives messages (AMQP protocol) in order to initiate it's logic flow.
My application may receive and handle more than 1 message at a time. I have an object that will be collecting information/data throughout the process, in several different services/classes, in order for me to use it at the end.
But I need the data to be seperated per message received, as a "Scoped" injection would seperate the injected instance from other HTTP requests.
My usecase is therefor very similar to how I would use a Scoped injected object in an ordinary API, but instead of a new HTTP request, I receive a message in my listeners.
Is there any way that I can create a custom scope, for every message received, either through some kind of configuration, or having the code create a new scope as the first thing in my Listener.MessageReceived(Message message) method?
Imagine a flow like this:
public class Listener {
ServiceClassA serviceClassA //injected in constructor
CustomLogger customLogger // (HAS TO BE SAME OBJECT INJECTED INTO ServiceClassA, ServiceClassB and Listener)
public void ReceiveMessage(Message message) {
using (var scope = CreateNewScope()) {
try {
serviceClassA.DoStuff();
} catch(Exception e) {
Console.Write(customLogger.GetLogs())
}
}
}
}
public class ServiceClassA {
ServiceClassB serviceClassB //injected in constructor
CustomLogger customLogger //(HAS TO BE SAME OBJECT INJECTED INTO ServiceClassA, ServiceClassB and Listener)
public void DoStuff() {
customLogger = ResolveCustomLogger(); // how do I make sure I can get/resolve the same object as in Listener (without having to pass parameters)
var data = // does stuff
customLogger.Log(data);
serviceClassB.DoStuff();
}
}
public class ServiceClassB {
CustomLogger customLogger //(HAS TO BE SAME OBJECT INJECTED INTO ServiceClassA, ServiceClassB and Listener)
public void DoStuff() {
customLogger = ResolveCustomLogger(); // how do I make sure I can get/resolve the same object as in Listener (without having to pass parameters)
var data = // does other stuff
customLogger.Log(data);
}
}
My CustomLogger may not only be used 1 or 2 service layers down, there might be many layers, and I might only want to use the CustomLogger in the bottom on, yet I want it accessible in the top level afterwards, to retrieve the data stored in it.
Thank you very much.
You can inject a ServiceScopyFactory in the class that reacts to messages from the queue, then for each message it receives it can create a scope, from which it requests a MessageHandler dependency.
The code sample below does exactly this (and it also deals with sessions on the queue, but that should make no difference for creating the scope).
public class SessionHandler : ISessionHandler
{
public readonly string SessionId;
private readonly ILogger<SessionHandler> Logger;
private readonly IServiceScopeFactory ServiceScopeFactory;
readonly SessionState SessionState;
public SessionHandler(
ILogger<SessionHandler> logger,
IServiceScopeFactory serviceScopeFactory,
string sessionId)
{
Logger = logger;
ServiceScopeFactory = serviceScopeFactory;
SessionId = sessionId
SessionState = new SessionState();
}
public async Task HandleMessage(IMessageSession session, Message message, CancellationToken cancellationToken)
{
Logger.LogInformation($"Message of {message.Body.Length} bytes received.");
// Deserialize message
bool deserializationSuccess = TryDeserializeMessageBody(message.Body, out var incomingMessage);
if (!deserializationSuccess)
throw new NotImplementedException(); // Move to deadletter queue?
// Dispatch message
bool handlingSuccess = await HandleMessageWithScopedHandler(incomingMessage, cancellationToken);
if (!handlingSuccess)
throw new NotImplementedException(); // Move to deadletter queue?
}
/// <summary>
/// Instantiate a message handler with a service scope that lasts until the message handling is done.
/// </summary>
private async Task<bool> HandleMessageWithScopedHandler(IncomingMessage incomingMessage, CancellationToken cancellationToken)
{
try
{
using IServiceScope messageHandlerScope = ServiceScopeFactory.CreateScope();
var messageHandlerFactory = messageHandlerScope.ServiceProvider.GetRequiredService<IMessageHandlerFactory>();
var messageHandler = messageHandlerFactory.Create(SessionState);
await messageHandler.HandleMessage(incomingMessage, cancellationToken);
return true;
}
catch (Exception exception)
{
Logger.LogError(exception, $"An exception occurred when handling a message: {exception.Message}.");
return false;
}
}
private bool TryDeserializeMessageBody(byte[] body, out IncomingMessage? incomingMessage)
{
incomingMessage = null;
try
{
incomingMessage = IncomingMessage.Deserialize(body);
return true;
}
catch (MessageDeserializationException exception)
{
Logger.LogError(exception, exception.Message);
}
return false;
}
}
Now whenever a MessageHandlerFactory is instantiated (which happens for each message received from the queue), any scoped dependencies requested by the factory will live until the MessageHandler.HandleMessage() task finishes.
I created a message handler factory so that the SessionHandler could pass non-DI-service arguments to the constructor of the MessageHandler (the SessionState object in this case) in addition to the DI-services. It is the factory who requests the (scoped) dependencies and passes them to the MessageHandler. If you are not using sessions then you might not need the factory, and you can instead fetch a MessageHandler from the scope directly.
When a user comes back to their session I have a listener check for some get parameters (denoting authentication). The problem is I can't reinitialize UI logic from that point (I believe) because it's not within a UIDL transaction. At any rate, my UI throws as NPE as soon as it hits the first call for UI.getCurrent().
How can I get a safe hook into each page refresh?
Vaadin 7.2 introduced a method called refresh(VaadinRequest) in UI which is called after a browser refresh when the UI has the #PreserveOnRefresh annotation.
I was unable to find a simple 'refresh' listener, but managed to get the behaviour by combining a RequestHandler and a UriFragmentChangedListener.
ui.getPage().addUriFragmentChangedListener(new Page.UriFragmentChangedListener() {
#Override
public void uriFragmentChanged(final Page.UriFragmentChangedEvent event) {
// ...proceed with application setup
}
});
ui.getSession().addRequestHandler(new RequestHandler() {
#Override
public boolean handleRequest(VaadinSession session, VaadinRequest request, VaadinResponse response) {
if (containsAuthInfo(request)) {
final String redirect = "<html><body></body><script>window.location=\""
+ APP_URL + "#" + UUID.randomUUID() + "\";</script></body></html>";
response.getOutputStream().write(redirect.getBytes());
return true;
}
return false;
}
});
I am new to blackberry development and I am creating a native blackberry application. On every screen of my application, I need to send and receive data to the server on the same connection.
What I have done so far is I have made a ConnectToServer class which has a bunch of methods for sending and receiving. I instantiate it on the main screen and I pass it to each screen as a parameter.
That class in not a thread because I only read and write when the user types in information and presses a button. So basically I am using the inputStream and outputStream on the event thread which I hear is BAD. Then I ask ConnectToServer to get me what the server sent. For instance, I get a vector which I use to make a ListField.
How can I make these UI updates?
public class Screen3 extends MainScreen {
ConnectToServer con;
Vector v;
public Screen3(String exerciseName, ConnectToServer connect)
{
con = connect;
con.send(exerciseName);
v = con.receiveVector();
mylist = new listField();
mylist.setSize(v.size());
add(mylist);
}
public void drawListRow(...)
{
graphics.drawText((String) v.elementAt(index)
}
}
So, there's many ways to approach this. First of all, since it seems like you only want one instance of ConnectToServer, and you are currently having to pass that around, you might try making that class a Singleton object. This is not necessary, and does not have anything to do with your threading problem, but I only offer it as a solution, for situations where you want to enforce that there's only one instance of something, and want to avoid having to pass it around everywhere. A simple Singleton implementation might be this:
public class ConnectToServer {
private static ConnectToServer _instance;
/** use this static method to get the one and only instance */
public static ConnectToServer getInstance() {
if (_instance == null) {
_instance = new ConnectToServer();
}
return _instance;
}
/** private to enforce Singleton pattern */
private ConnectToServer() {
}
}
And use it in your screens like this (no need to pass it into the constructor any more):
ConnectoToServer connection = ConnectToServer.getInstance();
connection.blahBlahBlah();
Now, on to the threading problem. You're right that you should not be performing network requests on the main (aka "UI", aka "Event") thread. If you have a nice separate ConnectToServer class, that makes it easier to encapsulate this behaviour. Instead of UI clients using a synchronous send() and receiveVector() method, make one method that just kicks off the request, and another callback method that the ConnectToServer class will call when the response comes back. The ConnectToServer class will use a Thread to perform this work, and thus avoid freezing the UI during the request.
I'll define an interface that the UI clients will implement:
public interface RequestListener {
/** listeners must implement this method to get data. method will be called on the UI thread */
void onDataReceived(Vector response);
}
And then the new (partial) ConnectToServer class:
public class ConnectToServer {
private Thread _worker;
private RequestListener _listener;
public void setRequestListener(RequestListener listener) {
// note: this implementation only allows one listener at once.
// make it a list if you need something more
_listener = listener;
}
/** initiate a network request on a background thread */
public void sendRequest(final String request) {
_worker = new Thread(new Runnable() {
public void run() { // run on the background/worker thread
send(request);
final Vector response = receiveVector();
if (_listener != null) {
// this assumes all our listeners are UI objects, so we pass
// data back to them on the UI thread:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() { // run on UI thread
_listener.onDataReceived(response);
}
});
}
}
});
_worker.start();
}
}
Note that you should also make your original send() and receiveVector() methods in this class private. They should only be called from inside the class now, not directly from UI clients.
Then, you need to code your Screen classes like this:
public class Screen3 extends MainScreen implements RequestListener {
public Screen3(String exerciseName) {
ConnectToServer connection = ConnectToServer.getInstance();
connection.setRequestListener(this);
// kick off the request (on a background thread)
connection.sendRequest(exerciseName);
}
public void onDataReceived(Vector response) {
if (mylist == null) {
// first time data has been received, so create and add the list field:
mylist = new listField();
add(mylist);
}
mylist.setSize(response.size());
// TODO: presumably, you would copy the contents of 'response' into 'mylist' here
}
}
Also, you might also want to code the server class to protect against multiple UI clients making concurrent requests, allow current requests to be cancelled, etc. But the above should get you started on a solution that provides a responsive app, without freezing your UI.