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 toapplication.properties
. -
All
jmix.liquibase.*
application properties are renamed tomain.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:
-
TabSheet →
JmixTabSheet
(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>
-
MultiSelectComboBox →
JmixMultiSelectComboBox
(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>
-
Upload →
FileStorageUploadField
(fileStorageUploadField
in XML) andFileUploadField
(fileUploadField
in XML)<fileStorageUploadField id="uploadField" dataContainer="userDc" property="picture"/>
-
Image →
JmixImage
(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.
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.
Liquibase Properties
-
jmix.liquibase.*
prefix has been renamed tomain.liquibase.*
in order to conform to the datasource properties naming pattern (e.g.main.datasource.url
wheremain
is the data store name). If you add asecond
data store, its Liquibase configuration properties will besecond.liquibase.*
. -
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
-
The
getColumns()
method ofDataGrid
andTreeDataGrid
now only returns columns that are not hidden by security. Previously, it returned all columns including hidden ones. -
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.*
.
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'
}
}