How is this even possible?! Vaadin has blown my mind!

Full source code is up on [GitHub here]
(https://github.com/bitbythecron/vaadin-spring-example). I am playing with Vaadin 13 for the first time and am building a simple “Supers App” where you can CRUD super heroes/villains and their Powers. Just something fun to mess around with.

The main view/page for the app is ManagementView which looks like this:

@Route(value = "")
public class ManagementView extends VerticalLayout {

    private Tabs tabs;
    private DataService dataService;

    public ManagementView(DataService dataService) {
        this.tabs = new Tabs();
        this.dataService = dataService;

        init();
    }

    private void init() {

        // Supers Pain
        HorizontalLayout supersPaneLayout = new HorizontalLayout();
        supersPaneLayout.setMargin(true);
        supersPaneLayout.setSizeFull();

        TextField tfNickname = new TextField("Nickname");
        tfNickname.setPlaceholder("e.g. \"Big Bear\"");

        ListBox<String> lbSuperTypes = new ListBox<>();
        lbSuperTypes.setItems("Hero", "Villain");

        List<String> powers = dataService.fetchAllPowers();
        VerticalLayout cbLayout = new VerticalLayout();
        for (String power : powers) {
            Checkbox cbPower = new Checkbox();
            cbPower.setLabel(power);

            cbLayout.add(cbPower);
        }

        Button btnAddSuper = new Button("Add", new Icon(VaadinIcon.PLUS));
        btnAddSuper.addClickListener(new AddSuperClickListener(tfNickname, lbSuperTypes, cbLayout, dataService));

        supersPaneLayout.add(tfNickname, lbSuperTypes, cbLayout, btnAddSuper);

        ListBox<String> lbPowers = new ListBox<>();
        lbPowers.setItems(powers);
        lbPowers.setVisible(false);

        Div masterDiv = new Div(supersPaneLayout, lbPowers);

        Tab tbSupers = new Tab("Supers");
        Tab tbPowers = new Tab("Powers");

        tbSupers.setFlexGrow(2);
        tbPowers.setFlexGrow(2);


        Map<Tab, Component> tabsToPages = new HashMap<>();
        tabsToPages.put(tbSupers, supersPaneLayout);
        tabsToPages.put(tbPowers, lbPowers);

        Set<Component> pagesShown = Stream.of(supersPaneLayout)
                .collect(Collectors.toSet());
        tabs.addSelectedChangeListener(event -> {
            pagesShown.forEach(page -> page.setVisible(false));
            pagesShown.clear();
            Component selectedPage = tabsToPages.get(tabs.getSelectedTab());
            selectedPage.setVisible(true);
            pagesShown.add(selectedPage);
        });


        tabs.setFlexGrowForEnclosedTabs(2);

        tabs.add(tbSupers, tbPowers);


        setHorizontalComponentAlignment(Alignment.CENTER, tabs);

        add(tabs);
        add(masterDiv);

    }
}

As you can see I’m injecting a DataService whose abridged version looks like this:

@Component
public class DataService {

    private Map<String,Super> supersMap;
    private List<String> powersList;

    public DataService() {
        this.supersMap = new ConcurrentHashMap<>();
        this.powersList = new ArrayList<>();

        powersList.addAll(Arrays.asList(
            "Flying",
            "Invisibility",
            "Super Strength",
            "Super Speed",
            "Fast Healing",
            "Pyrotechnics",
            "Cryotechnics",
            "Electrotechnics"
        ));

    }

    public void createSuper(Super toCreate) {

        if (!supersMap.containsKey(toCreate.getNickname())) {

            supersMap.put(toCreate.getNickname(), toCreate);

            System.out.println("New super added to the store!");

        }

    }
	
	// ... some other methods down here
}

And this createSuper() method is called from inside ManagementView’s click listener whose code is:

    @Override
    public void onComponentEvent(ClickEvent<Button> event) {

        String nickname = tfNickname.getValue();
        String superType = lbSuperTypes.getValue();

        List<String> powers = new ArrayList<>();
        List<Component> componentList = cbLayout.getChildren().filter(child -> child instanceof Checkbox).collect(Collectors.toList());
        List<Checkbox> cbPowers = componentList.stream().map(cb -> (Checkbox) cb).collect(Collectors.toList());
        powers.addAll(cbPowers.stream().map(cbPower -> cbPower.getLabel()).collect(Collectors.toList()));

        Super newSuper = new Super(nickname, SuperType.lookup(superType), powers);

        System.out.println("Adding a new Super");

        dataService.createSuper(newSuper);

    }

Now, I fully expected Vaadin to throw runtime exceptions when I ran my app; I figured since DataService is living on the server, and the ManagementView is cross-compiled into client-side HTML/CSS/JS, that when the AddSuperClickListener fired, that the whole thing would explode and throw an exception…

Instead, everything works PERFECTLY FINE!!! I can fill out the form underneath the Supers tab and press the “Add” button, and in the terminal, sure enough, I see the following console output:

Adding a new Super
New super added to the store!

How is this possible?!?! What is going on here that allows this to work? Is Vaadin somehow automagically creating an AJAX request between the browser and the server? I have so many questions!

Is Vaadin somehow automagically creating an AJAX request between the browser and the server?

Yes, that is what is exactly happening. Idea of Vaadin framework is to abstract away the server - client communication when you are using stock components or integrating templates to be used with Java.

mind == blown!

Thanks @Tatu! A few quick followup questions if you don’t mind:

  1. Are there any recommended “best practices” in Vaadin for separating client/server concerns? One obvious one I can think of is to put server stuff in a server or services package and client stuff under a client or ui package, and perhaps any domain/POJOs that get sent back and forth between client and server under a shared package. But I’m interested in any other tricks/tips/strategies this community has to offer!
  2. Does this mean that I generally don’t need to worry about crafting my own AJAX requests from Java code? Sounds like Vaadin takes care of all of this for me!
  3. Although this topic would be too advanced for me at the present time, since I’m so new to Vaadin, it may become something I’m interested in down the road: whatever mechanism Vaadin uses to build these “automagical” AJAX requests…does Vaadin provide an API for customizing/fine-tuning the AJAX?
  4. Any security concerns or configurations available for these AJAX requests? For instance what if I want to setup SSL/TLS on all client/server communication? Any other security concerns besides TLS?

Thanks again!

I think questions 1) and 2) are to some extent answered here

https://vaadin.com/docs/v13/flow/introduction/introduction-overview.html

  1. Normally the typical case where you need to be aware of this is when you setup proxy in front of your application server. Then you need to configure necessary routings SSL/TLS communications as well as WebSockets if application is using Push.

Thanks again, is it safe to assume that if I have SSL/TLS properly setup for spring that Vaadin-generated AJAX will also use SSL/TLS?