What’s New

This section covers new features of Jmix framework and Studio 1.5, as well as some breaking changes to be aware of when upgrading from a previous version of the framework.

How To Upgrade

To create new projects with Jmix 1.5 or to upgrade an existing project, you need Studio 1.5 or later, so update your Jmix Studio plugin first.

The minimal supported IntelliJ IDEA version is now 2022.3.

See Upgrading Project section for how to upgrade your project using Studio. The automatic migration procedure makes the following changes in your project:

  • Updates the version of Jmix BOM which in turn defines versions of all dependencies.

  • Updates the version of Jmix Gradle plugin.

  • Updates the version of Gradle wrapper to 7.6 in gradle/wrapper/gradle-wrapper.properties.

  • Adds all necessary includes to the root Liquibase changelog file.

  • The main.liquibase.change-log property is added to application.properties.

  • All jmix.liquibase.* application properties are renamed to main.liquibase.*.

  • Additional data store Liquibase configurations are be updated (if any).

  • In a project with Flow UI, UiMinimalRole is created in the project.

  • If the project uses Grid Export Actions and/or GrapesJS add-ons, their artefact names are changed.

See also the full list of breaking changes that can affect your project after the upgrade.

New Features and Improvements

Root Liquibase Changelog with All Includes

The root Liquibase changelog for the main data store now contains include directives for changelogs of all add-ons used in the project. It makes the changelog compatible with the Liquibase Gradle plugin and Liquibase CLI and allows you to run Liquibase outside the Studio or the application, for example on a CI server.

The project must have the main.liquibase.change-log property set to the root changelog path, for example:

main.liquibase.change-log=com/company/demo/liquibase/changelog.xml

Studio maintains the includes automatically when you add or remove add-ons. Also, when you start the application, it checks that includes in the root changelog correspond to the add-ons used in the project. If it finds a mismatch, Studio shows a notification dialog and suggests to add or remove includes. You can ignore some paths, and the notification won’t be shown again for the ignored items.

The Studio version migration procedure updates the root changelog and sets the application property automatically.

Read about breaking changes below .

Flow UI Core

Flow UI module has been upgraded to Vaadin 23.3.

The following Vaadin components were integrated into Flow UI:

  • TabSheetJmixTabSheet (tabSheet in XML)

    <tabSheet width="100%">
        <tab id="mainTab" label="Main">
            <formLayout id="form" dataContainer="userDc">...</formLayout>
        </tab>
        <tab id="additionalTab" label="Onboarding steps">
            <vbox>
                <hbox>...</hbox>
                <dataGrid width="100%" dataContainer="stepsDc">...</dataGrid>
            </vbox>
        </tab>
    </tabSheet>
  • MultiSelectComboBoxJmixMultiSelectComboBox (multiSelectComboBox in XML)

    <instance id="productDc"
              class="com.company.demo.entity.Product">
        <fetchPlan extends="_base">
            <property name="tags" fetchPlan="_base"/>
        </fetchPlan>
        <loader/>
    </instance>
    <collection class="com.company.demo.entity.Tag" id="allTagsDc">
        <fetchPlan extends="_base"/>
        <loader id="allTagsDl">
            <query>
                <![CDATA[select e from Tag e]]>
            </query>
        </loader>
    </collection>
    <!-- ... -->
    <formLayout id="form" dataContainer="productDc">
        <textField id="nameField" property="name"/>
        <multiSelectComboBox property="tags" itemsContainer="allTagsDc"/>
    </formLayout>
  • UploadFileStorageUploadField (fileStorageUploadField in XML) and FileUploadField (fileUploadField in XML)

    <fileStorageUploadField id="uploadField"
            dataContainer="userDc" property="picture"/>
  • ImageJmixImage (image in XML)

    <image id="image"
            dataContainer="userDc" property="picture"
            height="280px" width="200px" classNames="user-picture"/>
  • Many components now support the Tooltip, for example:

    <textField id="nameField" property="name">
        <tooltip text="Product name" position="BOTTOM_START"/>
    </textField>

    In Studio, tooltips are added using the Add button in the Jmix UI inspector panel.

Input Dialog

The Dialogs interface now has the createInputDialog() method which allows you to show input dialogs with arbitrary parameters. For example:

dialogs.createInputDialog(this)
        .withParameters(
                stringParameter("name").withLabel("Name"),
                intParameter("age").withLabel("Age"))
        .withCloseListener(dialogCloseEvent -> {
            if (dialogCloseEvent.closedWith(DialogOutcome.OK)) {
                System.out.println("Name: " + dialogCloseEvent.getValue("name"));
                System.out.println("Age: " + dialogCloseEvent.getValue("age"));
            }
        })
        .open();

Background Tasks

Flow UI now has the mechanism for executing asynchronous tasks without blocking the user interface, same as in Classic UI.

Example of running a task and displaying the progress in a label:

@Autowired
private BackgroundWorker backgroundWorker;

@ViewComponent
private Span taskProgress;

@Subscribe("testBtn")
public void onTestBtnClick(ClickEvent<Button> event) {
    BackgroundTaskHandler<Void> handler = backgroundWorker.handle(new SampleTask(15, null, 10));
    handler.execute();
}

protected class SampleTask extends BackgroundTask<Integer, Void> {
    int count;

    public SampleTask(long timeoutSeconds, View<?> view, int count) {
        super(timeoutSeconds, view);
        this.count = count;
    }

    @Override
    public Void run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
        for (int i = 1; i < count + 1; i++) {
            Thread.sleep(1000);
            taskLifeCycle.publish(i);
        }
        return null;
    }

    @Override
    public void progress(List<Integer> changes) {
        taskProgress.setText(changes.get(0) + "");
    }
}

Example of running a task and displaying the progress in a modal dialog:

@Autowired
private Dialogs dialogs;

@Subscribe("testBtn")
public void onTestBtnClick(ClickEvent<Button> event) {
    dialogs.createBackgroundTaskDialog(new SampleTask(15, this, 10))
            .withTotal(10)
            .withCancelAllowed(true)
            .open();
}

protected class SampleTask extends BackgroundTask<Integer, Void> {
    int count;

    public SampleTask(long timeoutSeconds, View<?> view, int count) {
        super(timeoutSeconds, view);
        this.count = count;
    }

    @Override
    public Void run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
        for (int i = 1; i < count + 1; i++) {
            Thread.sleep(1000);
            taskLifeCycle.publish(i);
        }
        return null;
    }
}

Flow UI in Add-ons

Flow UI modules are now available for the following add-ons:

  • Multitenancy

  • Quartz

  • Application Settings

  • Grid Export Actions (with the ability to export all rows, see Excel Export).

Flow UI Menu Designer

The "single mode" of the Flow UI menu designer has been significantly improved. When you switch to the single mode, Studio still provides the ability to add inherited menu items from the add-ons included in the project. All available items are always displayed in the separate panel on the left, and you can easily drag-and-drop them into your current custom menu.

Excel Export

The Grid Export Actions add-on now allows users to export to Excel all rows for the currently selected criteria. The excelExport action shows a dialog with All rows | Current page | Selected rows options.

Previously, it could export only rows visible on the current page or selected rows.

Pessimistic Locking UI

Pessimistic locking management UI has been added to the core UI modules. You can find it in the Administration menu of Classic UI and the System menu of Flow UI.

UI Designer Tool Window

The designer for both Classic and Flow UI now has a single tool window on the right: Jmix UI. It includes the components hierarchy and the inspector of the selected component properties.

The component palette is opened when you click Add component in the hierarchy context menu, in the XML descriptor top panel or Generate menu.

Code Snippets

The Code Snippets palette is now available by clicking the Code Snippets button in the editor window top panel for Spring beans and UI controllers.

The tool window has been removed because it has proven to be hard to find and thus rarely used.

Preview Features

Flow UI Generic Filter

The GenericFilter component (genericFilter in XML) allows users to filter data by arbitrary conditions created at runtime.

Usage example:

<facets>
    <dataLoadCoordinator auto="true"/>
    <queryParameters>
        <genericFilter component="filter"/>
    </queryParameters>
</facets>
<layout>
        <genericFilter id="filter" dataLoader="usersDl"
                       summaryText="My filter">
            <responsiveSteps>
                <responsiveStep minWidth="0" columns="1"/>
                <responsiveStep minWidth="800px" columns="2"/>
                <responsiveStep minWidth="1200px" columns="3"/>
            </responsiveSteps>
        </genericFilter>

The genericFilter element of the queryParameters facet is needed for reflecting the filter state in the URL query parameters. It ensures the same filter is applied when navigating back to the list view from a detail view, and when the user refreshes the browser page.

Currently, the generic filter supports only property conditions. All other features of the classic UI filter (JPQL and custom conditions, grouping, ability to save configurations) will be implemented in the next release.

Breaking Changes

Changed Add-on Artefacts

Grid Export Actions

Changed the starter artefact name:

  • Was io.jmix.ui:io.jmix.ui:jmix-ui-export-starter

  • Now io.jmix.gridexport:jmix-gridexport-ui-starter

And the base package:

  • Was io.jmix.uiexport

  • Now io.jmix.gridexportui

The Studio migration procedure automatically changes the artefact names in build.gradle, but you may need to change package names in your code manually if you get compilation errors.

GrapesJS

Changed the starter artefact name:

  • Was io.jmix.grapesjs:jmix-grapesjs-starter

  • Now io.jmix.grapesjs:jmix-grapesjs-ui-starter

and the theme artefact name:

  • Was io.jmix.grapesjs:jmix-grapesjs

  • Now io.jmix.grapesjs:jmix-grapesjs-ui

Quartz

Additional starter is now required for UI: io.jmix.quartz:jmix-quartz-ui-starter

Liquibase Properties

  1. jmix.liquibase.* prefix has been renamed to main.liquibase.* in order to conform to the datasource properties naming pattern (e.g. main.datasource.url where main is the data store name). If you add a second data store, its Liquibase configuration properties will be second.liquibase.*.

  2. application.properties must contain the explicit path to the root Liquibase changelog for each data store. For example:

    main.liquibase.change-log=com/company/demo/liquibase/changelog.xml
    
    second.liquibase.change-log=com/company/demo/liquibase/second-changelog.xml

Data Store Configurations

Additional datastore configuration must be changed.

The LiquibaseChangeLogProcessor class has been removed.

Previously, if we defined an additional data store and set DB Schema Management to Create and Update then Studio generated a bean definition like this:

@Bean
public SpringLiquibase thirdLiquibase(
            LiquibaseChangeLogProcessor processor,
            @Qualifier("thirdDataSource") DataSource dataSource) {
   return JmixLiquibaseCreator.create(
                dataSource,
                new LiquibaseProperties(),
                processor,
                "third");
}

A new bean definition must look as follows ("third" here is the data store name):

@Bean("thirdLiquibaseProperties")
@ConfigurationProperties(prefix = "third.liquibase")
public LiquibaseProperties thirdLiquibaseProperties() {
   return new LiquibaseProperties();
}

@Bean("thirdLiquibase")
public SpringLiquibase thirdLiquibase(
            @Qualifier("thirdDataSource") DataSource dataSource,
            @Qualifier("thirdLiquibaseProperties") LiquibaseProperties liquibaseProperties) {
    return JmixLiquibaseCreator.create(
                dataSource,
                liquibaseProperties);
}

Reference to Message in Formatters

We have fixed the incorrect handling of references to externalized messages in formatters defined in XML.

Now, as for any other messages, the msg://myFormat reference will look for a message with the group of the current screen, for example com.company.app.screen.foo/myFormat. The prefix with the triple slash will look for a message without group, for example myFormat.

To adapt your project to this change, find all usages of externalized messages in formatters and change double slashes to triple ones. For example:

<formatter>
     <date format="msg:///myDateFormat"/>
</formatter>

UiMinimalRole in projects with Flow UI

UiMinimalRole defines access to login and main views. To be able to change these views, the role has been moved from the framework to the projects. For example:

package com.company.demo.security;

import io.jmix.core.entity.KeyValueEntity;
import io.jmix.security.model.*;
import io.jmix.security.role.annotation.*;
import io.jmix.securityflowui.role.annotation.ViewPolicy;

@ResourceRole(name = "Flow UI: minimal access", code = UiMinimalRole.CODE, scope = SecurityScope.UI)
public interface UiMinimalRole {

    String CODE = "flowui-minimal";

    @ViewPolicy(viewIds = "MainView")
    void main();

    @ViewPolicy(viewIds = "LoginView")
    @SpecificPolicy(resources = "flowui.loginToUi")
    void login();

    @EntityPolicy(entityClass = KeyValueEntity.class, actions = EntityPolicyAction.READ)
    @EntityAttributePolicy(entityClass = KeyValueEntity.class, attributes = "*", action = EntityAttributePolicyAction.VIEW)
    void keyValueEntity();
}

Flow UI DataGrid

  1. The getColumns() method of DataGrid and TreeDataGrid now only returns columns that are not hidden by security. Previously, it returned all columns including hidden ones.

  2. Columns hidden by security do not change their visibility property. Previously, their visible attribute was being set to false.

ui-test-assist

The ui-test-assist module doesn’t bring transitive dependencies on Spock and Groovy anymore. Also, it does not contain UiTestAssistSpecification, ScreenSpecification and TestMainScreen classes.

All Spock and Groovy stuff has been moved to the new ui-test-assist-spock module.

If your project contains tests based on ScreenSpecification or UiTestAssistSpecification, add the following dependency to build.gradle:

testImplementation 'io.jmix.ui:jmix-ui-test-assist-spock'

and change imports of ScreenSpecification, UiTestAssistSpecification, TestMainScreen classes to io.jmix.ui.testassistspock.*.

Decimal Format

The default localized format for decimal data type has been changed to #,##0.00. If you want to return the previous value #,##0.##, add it to your message bundle:

decimalFormat=#,##0.##

Known Issues

Jmix 1.5.3 depends on Spring Boot 2.7.13, which in turns depends on Hazelcast version 5.1.7. Unfortunately, Hazelcast 5.1.7 has a bug. This bug makes it impossible to work with the Kubernetes cluster in embedded mode, and instructions described in the Kubernetes Cluster section don’t work.

The workaround is to downgrade the Hazelcast to 5.1.5. You can achieve this by adding the following instruction in the build.gradle file:

implementation('com.hazelcast:hazelcast') {
    version {
        strictly '5.1.5'
    }
}

Changelog