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

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 */);
});
}
}

Related

Prevent tab switching

I use Vaadin23.
Is there a legal way to prevent tab switching?
As i understand documentation method
public Registration addSelectedChangeListener(
ComponentEventListener<SelectedChangeEvent> listener) {
return addListener(SelectedChangeEvent.class, listener);
}
register listener which is triggered after tab change event.
What are the options?
Thanks!
If your tab contents are navigation targets (Routes), you can use the BeforeLeaveObserver interface in your form view:
public class SignupForm extends Div
implements BeforeLeaveObserver {
#Override
public void beforeLeave(BeforeLeaveEvent event) {
if (hasChanges()) {
ContinueNavigationAction action =
event.postpone();
ConfirmDialog confirmDialog = new ConfirmDialog();
confirmDialog.setText("Your form has changes! Are you sure you want to leave?");
confirmDialog.setCancelable(true);
confirmDialog.addConfirmListener(__ -> action.proceed());
confirmDialog.open();
}
}
private boolean hasChanges() {
// TODO: implement your logic to check if there are unsaved changes
}
}

Custom Listeners with detach() method error

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.

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
}
}

Changes in properties using beforeUpdate seems to have no effect on extended classes?

we have the following domain class inheritance strategy.
AbstractDomain (containts default properties)
User extends AbstractDomain
Operator extends User
In the AbstractDomain we have made methods to implement the
beforeUpdate and beforeInsert using methods so we can extends these on
extending classes
AbsrtractDomain
abstract class AbstractDomain {
protected void onBeforeInsert() {
...
}
protected void onBeforeUpdate() {
...
}
def beforeInsert() {
onBeforeInsert()
}
def beforeUpdate() {
onBeforeUpdate()
}
}
In the user class we have the logic the encrypt the users password like this..
User
public class User extends AbstractDomain {
#Override
protected void onBeforeUpdate() {
super.onBeforeUpdate()
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
println "encoding password!!!!"
if (springSecurityService) { // added the if clause to ensure
that tests work correct!
password = springSecurityService.encodePassword(password)
}
}
}
Operator
public class Operator extends User {
// code omitted
}
So when i try to update a operator, is see the message "encoding
password!!!!" the property is set, but when i check the DB the
password is still in clear text ..
The changes i make seem to have no effect and seem not to be persisted.
Any clue what i may be missing?
Looking at the code... if you see the "encoding password!!!" text, but the password is not encoded, then either the springSecurityService property is NOT set, or the encodePassword function failed to work... this has nothing to do with inheritance. I would move your println INSIDE the if, since that's more accurate.

delete version number in url

How can I delete or hide the version number in the URL introduced in Wicket 1.5?
Mounting a page doesn't help.
http://localhost/MyPage/SubPage?0
In Application.init():
mount(new MountedMapperWithoutPageComponentInfo("/subpage", MyPage.class));
with the following Mapper class:
public class MountedMapperWithoutPageComponentInfo extends MountedMapper {
public MountedMapperWithoutPageComponentInfo(String mountPath, Class<? extends IRequestablePage> pageClass) {
super(mountPath, pageClass, new PageParametersEncoder());
}
#Override
protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
// do nothing so that component info does not get rendered in url
}
#Override
public Url mapHandler(IRequestHandler requestHandler)
{
if (requestHandler instanceof ListenerInterfaceRequestHandler ||
requestHandler instanceof BookmarkableListenerInterfaceRequestHandler) {
return null;
} else {
return super.mapHandler(requestHandler);
}
}
}
If you don't want the version number then you page should be completely stateless, the version number is meant for stateful pages. For instance if your page includes a form then you should use the stateless variant of the Form component, that is org.apache.wicket.markup.html.form.StatelessForm.
If your page is already completely stateless, you can give wicket a hint by invoking the org.apache.wicket.Page#setStatelessHint method.
The solution using a self-created MountedMapperWithoutPageComponentInfo class doesn't work for Wicket 6.13+, the page won't respond to callback user actions. (Note that there are multiple versions of MountedMapperWithoutPageComponentInfo on the Internet.)
A solution for 6.13+ (tested with 6.15) can be found here:
http://apache-wicket.1842946.n4.nabble.com/Delete-version-number-in-url-td4665752.html
https://svn.apache.org/repos/asf/openmeetings/trunk/singlewebapp/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
// Put this code in your WebApplication subclass
import org.apache.wicket.core.request.mapper.MountedMapper;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
import org.apache.wicket.request.mapper.info.PageComponentInfo;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.core.request.handler.BookmarkableListenerInterfaceRequestHandler;
private static class NoVersionMapper extends MountedMapper {
public NoVersionMapper(final Class<? extends IRequestablePage> pageClass) {
this("/", pageClass);
}
public NoVersionMapper(String mountPath, final Class<? extends IRequestablePage> pageClass) {
super(mountPath, pageClass, new PageParametersEncoder());
}
#Override
protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
//Does nothing
}
#Override
public Url mapHandler(IRequestHandler requestHandler) {
if (requestHandler instanceof ListenerInterfaceRequestHandler || requestHandler instanceof BookmarkableListenerInterfaceRequestHandler) {
return null;
} else {
return super.mapHandler(requestHandler);
}
}
}
Then you can mount pages using:
// Put this in the init() method of your WebApplication subclass
getRootRequestMapperAsCompound().add(new NoVersionMapper("urlPatternOfAPage", YourPage.class));
Use the following mapper to mount pages, this should work on any book markable page except the homepage.
Here's how to use the mapper in Application.init()
mount(new MountedMapperWithoutPageComponentInfo("/subpage", MyPage.class));
Here's the mapper.
import org.apache.wicket.request.Url;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.mapper.MountedMapper;
import org.apache.wicket.request.mapper.info.PageComponentInfo;
import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
public class MountedMapperWithoutPageComponentInfo extends MountedMapper {
public MountedMapperWithoutPageComponentInfo(String mountPath, Class<? extends IRequestablePage> pageClass) {
super(mountPath, pageClass, new PageParametersEncoder());
}
#Override
protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
// does nothing so that component info does not get rendered in url
}
}
For me the solution with setStatelessHint didn't work. The following did work:
class MyApplication extends WebApplication {
#Override protected void init() {
getRequestCycleSettings().setRenderStrategy(
IRequestCycleSettings.RenderStrategy.ONE_PASS_RENDER);
....
}
}
For Wicket 8, this NoVersionMapper class works:
https://github.com/apache/openmeetings/blob/master/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java#L314
public class NoVersionMapper extends MountedMapper {
public NoVersionMapper(final Class pageClass) {
this("/", pageClass);
}
public NoVersionMapper(String mountPath, final Class pageClass) {
super(mountPath, pageClass, new PageParametersEncoder());
}
#Override
protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
//Does nothing
}
#Override
public Url mapHandler(IRequestHandler requestHandler) {
if (requestHandler instanceof ListenerRequestHandler || requestHandler instanceof BookmarkableListenerRequestHandler) {
return null;
} else {
return super.mapHandler(requestHandler);
}
}
}
This is basically the same as Devabc's code but this one compiles on Wicket 8. It has been tested against known regressions of the previous versions of the code: Ajax works and no page refreshing is triggered when it shouldn't.
The workarounds suggested so far may work with specific releases and have side effects. They should be considered hacks. I have used these hacks and they were broken by new releases. Therefore I have created a request for generic framework support here (please comment / vote): setVersioned(false) should force single Page Version.
Another example of a side effect: Page Reload on Submit of non-versioned Page

Resources