Giter VIP home page Giter VIP logo

portlet's Introduction

Vaadin Portlet

This is the official Portlet 3.0 (JSR 362) integration for Vaadin 23.1+ applications.

Getting started

Vaadin Portlet Starter Project is a skeleton single-module project that can be used as a starting point for your own Vaadin Portlet application.

Demo

Address Book is a demo application showcasing Vaadin Portlet multi-module setup, component-based views and inter-Portlet communication.

Documentation

Read the documentation here.

portlet's People

Contributors

alvarezguille avatar artur- avatar caalador avatar dependabot[bot] avatar mcollovati avatar mehdi-vaadin avatar mshabarov avatar tatulund avatar tltv avatar ujoni avatar zhesun88 avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

portlet's Issues

Enable giving static files location/package url

Currently we generate a fixed name static war containing vaadin-bundle, flow-client and other common static files.

Portlet should in PortletWebComponentBootstrapHandler::modifyPath be able to take from the deploymentConfiguration the static files deployment target instead of using the hardcoded /vaadin-portlet-static/ that is built at the moment.

It's hard to realize that you are doing something wrong on the server side

I think this is a generic embedding issue for Flow (so it's not only portlet related issue).
But it's highly visible with portlets.

The problem is: is you are doing something wrong in your servers side code which throws an exception you are not able to understand that. The client side code doesn't show any exception/error message.
You may realize that something is wrong only reading the log message of you server but normally you don't monitor it all the time (in fact it's even hard to find where it's located).
The client side should at least show some notification that there is a server side error. Then you will get a point to look at the server log.
Would be better to show the exception stacktrace right away in the web component in a similar way how it's done in the plain Flow app.

I understand that this is a different workflow: we don't have control over the whole page but anyway there should be a way to show an error inside the embedded/portlet web component (e.g. via removing the content of the portlet/embedded component and replacing it via a custom component with the exception).

Steps to reproduce:

  • make a portlet
  • create a exporter with a component which throws an exception : e.g. use a button which throws an exception on click.
  • throws an exception in the portlet using your code

The result: there is no indication in the browser that the server side threw an exception.

Use e.g. this code:

public class FormPortlet extends VaadinPortlet {

    public static final String TAG = "form-portlet";

    @Override
    public String getMainComponentTag() {
        return TAG;
    }

}
@Theme(Lumo.class)
public class FormPortletExporter extends WebComponentExporter<Form> {

    public FormPortletExporter() {
        super(FormPortlet.TAG);
    }

    @Override
    protected void configureInstance(WebComponent webComponent, Form form) {
    }

}

public class Form extends Div {

public Form() {
   Button button = new Button("Throw an exception", event -> throw new RuntimeException());
   add(button);
}
}

Docs: How can I use public parameters with Vaadin Portlet (IPC)

TODO:

  • declaring supported public parameters for portlet
  • reading request parameters
    • in handlers
  • causing portlet requests with set public parameters using Vaadin Portlet API
    • for example, button click listener causes a portlet request with public parameters

Address book as two different portlets

Build a Demo of having address book split into two different portlets, one with the grid and another with the form.

The portlets should communicate between each other using IPC features.

Acceptance Criteria

  • portlet-support contains a demo module that contains a address-book in a feature branch
  • feedback from the dev team and an external (architect) has been gathered and considered
  • inform your team mates about the conclusion of the demo and then schedule a grooming for creating the issues to create the implementation and tutorial

Portlet not working when added to page.

There is a problem with a vaadin portlet not working when added to a page for Pluto 3.0.1

  • deploy vaadin portlet war
  • startup portal tomcat
  • in admin add vaadin portlet to JSR 286 Tests
  • go to JSR 286 Tests
  • See if portlet works. (should not work)
  • if not
  • restart server
  • See if portlet works on JSR 286 Tests (should work)

Release portlet-support 1.0.0.alpha1

  • Create release jobs on Bender for releasing portlet-support
  • Release portlet-support 1.0.0.alpha1 to pre-releases using the created release job.

IPC: provide a dedicated way to send events via portlet hub to the server

Related to #67.
Should be done when API is designed.

Once #33 is implemented there is a way to send an event which will be delivered to the server side. Such event should have "vaadin." prefix.
It means that only specific events are sent to the server side.

It might be that this way is not convenient (even though there is a way to register a custom client side event listener which may send an event to the server side, the correct implementation of such listener is quite not trivial).

We should either:

  • add a method which sends an event specifically to the server side via the hub using the existing method : it prefixes the event with "vaadin." and then just call the existing method.
  • the portlet should have a way to say which events it's interested in and it will receive them out of the box without having any magic prefix like "vaadin.". It should be able to add/remove event types(names) for which it wants to receive server side events. There should be a proper API for this. The implementation should : avoid registering any event listener initially (like it's done now), every time when portlet instance call a method to request receiving specific events the JS is executed which just register a listener to the hub.

API to set WindowState programmatically

There should be API for setting the portlet WindowState from the server.
The API should be easy to use from the portlet view component.

If portlet is in 3.0 mode the window state should be set through the PortletHub,
for 2.0 portlets we should have a fallback to use ActionURL or RenderURL with window state information.

For both 2.0 and 3.0 mode the server should probably implement and handle public void processAction(ActionRequest request, ActionResponse response)

Acceptance criteria

  • There is an api to set the current WindowState (suggestion setWindowState(WindowState))

Portlet window modes

Open questions about portlet spec's window modes:

  • Can a portlet maximize itself?
  • Can portlet X request portlet Y to be maximized?

Client messages should go to different urls for portlets

At the moment having 2 Vaadin portlets make a request on the same roundtrip
the messages will get bundled into the same rpc request.

With the registered endpoints for the 2 portlets being
/pluto/portal/Vaadin/__pdaddress-book-demo.FormPortlet%21-1740289581%7C1;0/__pdaddress-book-demo.GridPortlet%21-1740289581%7C0;1/__rs1/__clcacheLevelPage/__ri0x3uidl
/pluto/portal/Vaadin/__pdaddress-book-demo.FormPortlet%21-1740289581%7C1;0/__pdaddress-book-demo.GridPortlet%21-1740289581%7C0;1/__rs0/__clcacheLevelPage/__ri0x3uidl/__ws1;normal

We still get the request content for both portlets to the URL
/pluto/portal/Vaadin/__pdaddress-book-demo.FormPortlet%21-1740289581%7C1;0/__pdaddress-book-demo.GridPortlet%21-1740289581%7C0;1/__rs0/__clcacheLevelPage/__ri0x3uidl/__ws1;normal/__rv0;v-r:uidl/__rv0;v-uiId:0

Which makeds the FormPortlet request also go to the GridPortlet as for the FormPortlet the path part __rs0 should be __rs1 as registered.

Investigate how to produce frontend resource monolith for portlets

The assumed way for deploying multiple portlets is to follow what Vaadin 7/8 did for multiple portlets. That deployment model should be used to guide the investigation of packaging the resources.

How do we build a monolithic client-bundle jar from multiple portlets, which contains

  • all the front-end resources

Resource access investigation:

  • is there a way for a portlet to access resources from another portlets or wars deployed in the same context/web server

Remember to check how framework 7/8 solved this issue and can we reuse some parts of that implementation.

Acceptance criteria

  • Identify the way Vaadin wants to serve static files to Vaadin portlets and how to request those files.

Docs: How can I window state

  • enable window states for my portlet
  • react to window state change
  • change the window state for my portlet
  • change the window state for window portlet

Another page after #35

Implement Portlet Mode change Handling

public class Form extends FormLayout implements PortletModeHandler {

    @Override
    public void onPortletModeChange(PortletModeChangeEvent event) {
        binder.setReadOnly(event.isViewMode());
    }
}

Investigate how to keep portlet state through refreshes (page and render)

Vaadin portlets should survive page and render refresh events. Investigate what different refresh events are possible and whether @PreserveOnRefresh alone is sufficient to prevent state loss. If not, what are the issues that prevent it from working.

Acceptance criteria

  • Document all potential refresh events for portlets
    • bonus points for portlet 3 spec
  • Does @PreserveOnRefresh allow our portlet to survive all the refresh events
    • Didn't? What changes are required for it to be able to do so.
      • Have a meeting about these problems and ticketize

Produce an Vaadin portlet API proposal from a draft

During the meeting on 23.9.2019, Leif created a loose API draft based on the comments presented in the meeting.

The demonstrating code is as follows:

// Simplest possible - maybe not feasible because the portlet API is a mess
@VaadinPortletView("myPortlet")
public class MaximizedViewOrEditView extends MyComponent implements VaadinPortletView {
  @Override
  public void init(PortalComponentContext context) {
    this.addFooBarListener(event -> {
      context.sendEvent(some, parameters, here);
    });
    context.onPortletModeChange(mode -> this.setEdit(mode.equals(PortletMode.EDIT));
    context.onEvent((EventRequest request, EventResponse response) -> {
      this.setEventReceived(request.getEvent().getName));
    });
    boolean isAdmin = context.hasRole("admin");
    this.setAdminControlsAvailable(isAdmin);
  }
}
// Intermediate level with annotations
@PortletApplication(
    @EventDefinition(xyz)  
)
public class MyOtherPortlet {
  @RenderMethod("myPortlet")
  public String doThings(request) {
    if (maximised) {
      return VaadinPortlet.doThings(request, MaximizedViewOrEditView.class);
    } else {
      return "<img>";
    }
  }
  @EventMethod("someEvent")
  public void something(EventRequest req, EventResponse resp) {
    VaadinPortlet.dispatchToTheRightComponentInstance(req, resp, MaximizedViewOrEditView.class);
  }
}
// Intermediate level with xml
public class MyPortlet extends VaadinPortlet<MaximizedViewOrEditView> {
}
// Advanced case for using different components with separate @PreserveOnRefresh scopes for different render modes, but still using xml
public class MyPortlet extends VaadinPortlet {
  @Override
  protected Class<? extends Component> getRenderDefinition(PortletRequest request) {
    if (request.getPortletMode().equals(PortletMode.HELP)) {
      return HelpComponent.class;
    } else if (request.getWindowState().equals(WindowState.MAXIMIZED)) {
      return MaximizedViewOrEditView.class;
    }
  }
  doEdit(RenderRequest req, RenderRespones resp) {
    if (maximised) {
      super.doEdit(req, resp);
    } else {
      renderImg(resp);
    }
  }
}

This code should be formalized and a API draft should be produced in this repository. This API should then be used in the various demonstration projects to validate the design and find problems therein.

Make addressbook a public demo

First step:

  • Showcases the window state, portlet mode and IPC features [Mehdi]
  • The demo uses the features as they should be used according to the documentation [Mehdi]
  • There exists a public repository vaadin/addressbook-portlet
  • The demo uses named versions of platform, flow and vaadin-portlet (NO SNAPSHOTS)
  • Demo Available as a public repository vaadin/addressbook-portlet [Mehdi]
  • The demo is added to vaadin-portlet release checklist as a prerequisite to test the version to be released [mikael]
  • The portlet documentation overview links to this repository [Denis]
  • The readme explains what is the demo about and how one should hold it in their hand, and links to the portlet documentation overview page in github [Mehdi]

VaadinPortlet should not implement ExportsWebComponent

ExportsWebComponent adds extra contract to VaadinPortlet which is confusing in many ways.

  • First of all: the end user doesn't need to know about ExportsWebComponent methods in VaadinPortlet.
  • ExportsWebComponent contract is used is in a totally unrelated way to VaadinPortlet : the instance of ExportsWebComponent is created just to call its methods and then this instance is not used anyhow (this is embedded web components machinery). VaadinPortlet instance is created by the portlet container, this is portlet machinery. As a result there are two instances which has no any relation to each other and this may cause a lot of confusions and mistakes.

There is already confusion in the existing code:

 @Override
    public void configure(WebComponent<C> webComponent, C component) {
        if (VaadinPortlet.getCurrent() != null) {
            // Cannot use 'this' as it is only a temporary object created by
            // WebComponentExporter handling logic
            VaadinPortlet<C> thisPortlet = VaadinPortlet.getCurrent();
            thisPortlet.viewInstance = component;
        }
    }

You need some time to understand why you can't use this here.
And someone who is not aware of the reasons may implement other methods using this just because it's possible.
This is very error-prone.

API to set PortletMode programmatically

There should be API for setting the portlet PortletMode from the server.
The API should be easy to use from the portlet view component.

If portlet is in 3.0 mode the window state should be set through the PortletHub,
for 2.0 portlets we should have a fallback to use ActionURL or RenderURL with portlet mode information.

For both 2.0 and 3.0 mode the server should probably implement and handle public void processAction(ActionRequest request, ActionResponse response)

Acceptance criteria

There is an api to set the current PortletMode (suggestion set PortletMode(PortletMode))

Create and run DX test for the implemented features

Create a DX test that is based on the base portlet starter. Focus is on implementing the PortletView interface.

DX structure:

  • Highlight the holy trinity of our portlet features:
    • portlet mode
    • window state
    • portlet events
  • User creates a 2nd portlet in addition to the starter portlet
  • The 2nd portlet sends an event which is received by the 1st portlet
    The event should contain a value set by the user, and should affect the state of 1st portlet
  • One portlet should show-case portlet modes and the other window states.

Acceptance criteria

  • Device the DX test with a very simple real world example
  • The test should have 3 to 5 participants, with one test taking around 20 minutes
  • Produce DX test result documentation

Remove PortletHubUtil

The class has public utility methods for portlet hub access.

Several issues here:

  • We should not have a public class which is used only internally.
  • The methods use implicit context like UI.getCurrent() and VaadinPortletService.getCurrentResponse(): this is bad. The context should be passed as parameter(s) in the methods.
  • It seems that methods are not widely used across several classes. So there is no need to have them as utility methods. There is only one class which uses the methods. They should be moved there as private methods.

I've removed code which registers the portlet instance to the portlet hub.
It seems after this removal there is no need to share its methods anymore.

Docs: I can create Vaadin portlets

Based on #34

Acceptance Criteria

  • There is documentation in (asciidoc) that explains all the things that are required for creating a Vaadin portlet and deploying it (if there is any special considerations for deploying it)
  • The base-starter-flow-portlet has been updated to showcase the default starting point for building your own portlet

This will eventually end up online like https://github.com/vaadin/multiplatform-runtime-internal/tree/master/mpr-documentation so this should be in the portlet-support repository (this)

Implement IPC using Portlet Hub (3.0 spec)

For IPC event handling using the Portlet 3.0 PortletHub the portlets should firstly register themselves on to the hub and retain the hub registration object.
The JS for this would be:

if (!window.Vaadin.Flow.Portlets) {
    window.Vaadin.Flow["Portlets"] = {};
}
if (!window.Vaadin.Flow.Portlets.$0) {
    if (portlet) {
        portlet.register($0).then(function (hub) {
            window.Vaadin.Flow.Portlets[$0] = hub;
            hub.addEventListener('portlet.onStateChange', function () {
// Here could be some storing of state/state comparisons with an action event after hub has handled the event e.g. !hub.isInProgress()
            });
        });
    }
}

Where $0 is the portlet namespace (which is of type Pluto_address_book_demo_GridPortlet__1740289581_0_).

Then a portlet that wants to receive events would use the registration to register an listener as

var hub = window.Vaadin.Flow.Portlets[$0];
hub.addEventListener('selection', function (type, payload) {
    window.alert('EVENT');
});

Then the sending portlet would send an event as:

var hub = window.Vaadin.Flow.Portlets[$0];
var params = hub.newParameters();
params['action'] = ['send'];
params['selection'] = ['0'];
params['windowState'] = ['maximized'];
hub.dispatchClientEvent('selection', params);

This would then create an event "selection" that would be caught by the other portlet on the client that would then show an alert. Here we could generate a portal action

              if(!hub.isInProgress()) {
                var params = hub.newParameters();
                params['action'] = ['send'];
                params['selection'] = [payload.selection];
                hub.action(params);
              }

Events : make some interface whose method will be called once

See #67.

The interface is called PortletView there.
It should have one method (chose it from #67 or invent a better name).
This method accepts a context parameter .
The context parameter should allow add listeners and send events: that's the intention.

The implementation of context object method is not a part of this ticket.
There is already implementation for sending events (as a part of #33).
Implement one method to prove that the concept works (the method is called at the proper time).

So :

  • introduce the interface
  • call it's method if porltet component implements this method at the proper type.
  • if sendEvent (fireEvent) is called on the context then it should send an event as it's done now in #33.

Paper DX between Porlet-driven and Interface driven approaches

Gather preference data using paper examples of Vaadin Portlet API. Provide two examples implemented with both Portlet-driven and interface-driven styles. The examples should highlight the portlet API with as little business logic as possible. But the operation context should still be clear. The potential user looking at the example should be able to understand what is the use-case being presented, even if the business logic is missing.

Examples:

  • Build an example on window state driven portlet application. Normal mode displays an image, maximized displays a form.
  • Build an example based on the address book demo. Both portlets (contact list and contact information) should be displayed. Highlight IPC and portlet mode (for contact information portlet, for editing).

Portlet-driven:

Interface-driven:

IPC: reiterate the API for sending events and registering listeners

Once #33 is done there will be a bad API which allows to send events and register a client side JS listener.

There is a EventHandler API which allows to receive events.
But VaadinPortlet instance is used to send an event and register a listener.
The VaadinPortlet instance is stateless and kind of singleton.
So it's quite a bad place for such methods.

This ticket is about design decision how to send an event and register a listener properly.
The implementation ticket should follow up.
One of the suggestions is :

  • add some interface with one method. Method accepts a context parameter which allows to add an event listener/send an event, etc.
  • if a portlet component implements this interface then the method is called (within our framework) with a proper context impl .

That will allow to do all events actions within the component impl without going outside of it and use some magic methods somewhere.

Investigate differences between Portlet 2 and Portlet 3 spec

Investigate differences between Portlet 2 and Portlet 3 API specification and figure out the implications of those differences for Portlet 3 spec support

Acceptance criteria

Addressbook as two portlets demoing the features

It should be possible to checkout the code and see the features in action:

  • Grid Portlet and Form Portlet
  • The user can normalize/maximize the Portlets
  • The user can trigger edit mode for the Form Portlet
  • When the user selects a row in the Grid Portlet, the Form Portlet is updated using the IPC features (first it can be done with some hacks)
  • When the portlets are in maximized mode, an operation in either portlet should trigger the other portlet in maximized mode: e.g. if the user clicks Close button in the Form Portlet that is maximized, the Grid Portlet should be opened in maximized mode OR when clicking a row in the Grid Portlet when it is maximized, the form portlet is opened on the side (as maximized / normal ?)

Demonstrate window state handling in Vaadin portlets

Create a Vaadin portlet based on the draft created from #25 which demonstrates how the user can create a portlet which

  • Displays a single image in normal window mode
  • Displays a fictional user form in maximized view

The demonstration should seek to align itself with the new API draft but also seek ways to improve upon the API, to make the task as easy and clear for the potential user as possible.

Portlet mode demo

Create a demo showcasing usage of different portlet modes VIEW, EDIT and HELP

  • VIEW mode should show static data.
  • EDIT mode should be enabled only for an administrator user and there you could change the data shown in VIEW mode
  • HELP mode would show static html explaining how the portlet works.

Acceptance Criteria

  • portlet-support demo module contains a ModeDemo module in a feature branch
  • feedback from the dev team and an external (architect) has been gathered and considered
  • inform your team mates about the conclusion of the demo and then schedule a grooming for creating the issues to create the implementation and tutorial

Docs: WindowState

  • There is documentation which instructs how you can react to the window state change in your portlet (implementation: #32)
  • There is documentation which instructs how you can change the window state in e.g. a button click listener (implementation: #37)

There should exist a collection of Portlet - ViewInstance

For sending portlet events/states to the portlet view instance we would need a
Collection or EventHub that would connect Portlet View Instance with the Portlet NameSpace for the portlet so that when the VaadinPortlet needs to send some information it could
take the response NameSpace information and use that to inform the View Instance.

Portlet definition should not require user to implement WebComponentExporter

Currently, the content of a portlet is exported as a web component by adding an explicit WebComponentExporter subclass in parallel with the VaadinPortlet subclass.
This should not be required.

Acceptance criteria

  • The web component is exported behind the scenes, using the web component exporter feature
  • The web component tag (which needs to be unique for each VaadinPortlet subclass), is generated from the portlet name (defined in portlet.xml) or the subclass name (whichever is easier).
  • The abstract VaadinPortlet should be extends VaadinPortlet<VIEW extends Component> and take in the ViewClass so that the portlet can set up things by view interfaces

Address book demonstration version 2.0

Reiterate Address book demonstration (#19) to use the newer iteration of Vaadin portlet API defined by #25. Use the request parameter based IPC.

Address book has two portlets that communicate using Portlet spec IPC.

  • Portlets:
    • Address entry listing portlet (grid)
    • Address information view (form)
  • Functionality in Address information view:
    • View mode should only show data in the form but does not allow editing.
    • Edit mode allows the user to edit the address data being displayed.
    • Deleting the user is allowed only when in Edit mode and Admin role.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.