In Cuba, there was a separate global and core modules which we could share between a Web project and Portal project. This allowed us to build a Vaadin UI front-end or a portal project with Rest controllers to provide APIs to the same set of entities and services and deploy on separate URL or instances. How can we archieve the same thing in Jmix 2 ? We want to have a project that exposes a set of REST APIs without the Vaadin Flow UI but share the same business logic code.
Hello,
would this help
Kind regards,
Mladen
Hi CK,
Take a look at the REST DataStore add-on.
With the upcoming new feature Sharing entities and service interfaces between server and client · Issue #4624 · jmix-framework/jmix it will offer functionality similar to CUBA’s client/middleware tiers separation.
Regards,
Konstantin
Hi. Instead of “splitting” each project into sub-modules (UI, Services, etc), can we have the packager pick up the whole add-on (UI + services) but exclude views and the flow-ui libraries when adding to a REST Service application ?
Perhaps you can instead develop 2 add-ons for each “functional module”: backend and UI. The latter depends on the former.
Then in a full-stack application you will include both, and in a REST-only app you will include only the backend.
BTW this is how most of the Jmix standard add-ons are built.
Konstantin,
For the “backend” functional module, what type of project should it be ? In the new Jmix Project options, there is only Java Add-on and Kotlin Add-on. Which type should I pick if I only want the Entities and Services without any (flow) UI components that will be included ?
Also, in the backend module, if I need to send a Global UI event or use the UiEventPublisher to trigger a UI event in the case where I want to tell the user something has happened in the backend on the screen, will it require the UI components/libraries ?
Regards
CK
Create a Java/Kotlin add-on project and just remove the UI libraries from build.gradle and UI configuration code from the Spring configuration class:
archivesBaseName = 'sample-backend'
dependencies {
implementation 'io.jmix.core:jmix-core-starter'
implementation 'io.jmix.data:jmix-eclipselink-starter'
// implementation 'io.jmix.flowui:jmix-flowui-starter'
// implementation 'io.jmix.flowui:jmix-flowui-themes'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation 'org.springframework.boot:spring-boot-starter-web'
testRuntimeOnly 'org.hsqldb:hsqldb'
}
//configurations.implementation {
// exclude group: 'com.vaadin', module: 'hilla'
// exclude group: 'com.vaadin', module: 'hilla-dev'
// exclude group: 'com.vaadin', module: 'copilot'
//}
test {
useJUnitPlatform()
}
package com.company.samplebackend;
import io.jmix.core.annotation.JmixModule;
import io.jmix.core.impl.scanning.AnnotationScanMetadataReaderFactory;
import io.jmix.eclipselink.EclipselinkConfiguration;
//import io.jmix.flowui.FlowuiConfiguration;
//import io.jmix.flowui.sys.ActionsConfiguration;
//import io.jmix.flowui.sys.ViewControllersConfiguration;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import java.util.Collections;
@Configuration
@ComponentScan
@ConfigurationPropertiesScan
@JmixModule(dependsOn = {EclipselinkConfiguration.class/*, FlowuiConfiguration.class*/})
@PropertySource(name = "com.company.samplebackend", value = "classpath:/com/company/samplebackend/module.properties")
public class SmplbckConfiguration {
// @Bean("smplbck_SmplbckViewControllers")
// public ViewControllersConfiguration screens(final ApplicationContext applicationContext,
// final AnnotationScanMetadataReaderFactory metadataReaderFactory) {
// final ViewControllersConfiguration viewControllers
// = new ViewControllersConfiguration(applicationContext, metadataReaderFactory);
// viewControllers.setBasePackages(Collections.singletonList("com.company.samplebackend"));
// return viewControllers;
// }
//
// @Bean("smplbck_SmplbckActions")
// public ActionsConfiguration actions(final ApplicationContext applicationContext,
// final AnnotationScanMetadataReaderFactory metadataReaderFactory) {
// final ActionsConfiguration actions
// = new ActionsConfiguration(applicationContext, metadataReaderFactory);
// actions.setBasePackages(Collections.singletonList("com.company.samplebackend"));
// return actions;
// }
}
Send a regular ApplicationEvent
in the backend module. Listen to it in the UI module and re-send using UiEventPublisher
. To avoid the loop in the listener, check the event source and ignore if is this
.
Regards,
Konstantin
Thank Konstantin.
For the backend module, I have removed the other add-on libraries which has “flowui” in the package name e.g. “io.jmix.reports:jmix-reports-flowui-starter” in the gradle file and this works.
And for the UI module, I have kept the “flowui” libraries and removed the other starter packages and kept the *flowui-starter only in the cradle file. Is this correct ?
I am having trouble login in via the UI with this configuration, it complains that there is something wrong with my User table:
Exception Description: Problem compiling [select e from synergy$user e where e.username = :username].
[14, 26] The abstract schema type ‘synergy$user’ is unknown.
[35, 45] The state field path ‘e.username’ cannot be resolved to a valid type.
In the UI module, you can keep all dependencies as is, unless you are going to use it without backend (e.g. via REST DataStore).
The error you are mentioning is usually caused by the wrong configuration of custom modules. Check out this doc: Creating Add-ons :: Jmix Documentation. Pay attention to the application log output starting with Using Jmix modules:
.
If the problem persists, attach all build.gradle
, main Java configuration files with @JmixModule
and log output here.
Hi Konstantin,
I solved the login problem. It was caused by (a) using ‘implementation’ instead of ‘api’ to export the Core add-on to the higher up projects and (b) need to have “system-full-access” roles for both Views and Entities in the core for system user. I can now login and access the views and entities. I also solved the duplicate JmixModule ID issue when I split the single add-on into UI and Core.
My next issue is that I have a CustomCreateAction which extends the CreateAction. It has the @ActionType(CreateAction.ID) annotation defined. It works when both UI and Core are in one add-on, but when I split it, Jmix doesn’t seem to register and use the CustomCreateAction by default, only the base flow UI CreateAction. Why the difference ? I am getting:
java.lang.ClassCastException: class io.jmix.flowui.action.list.CreateAction cannot be cast to class com.infopro.synergy.web.actions.CustomCreateAction (io.jmix.flowui.action.list.CreateAction and com.infopro.synergy.web.actions.CustomCreateAction are in unnamed module of loader ‘app’)
Konstantin,
Sorry. My colleague found the problem. There is a ActionConfiguration bean which seems to require a more targeted basePackage name. When I use com.infopro.synergy, it doesn’t pick up the Custom Action classes. Needs to use a lower package name: com.infopro.synergy.web.
final ActionsConfiguration actions = new ActionsConfiguration(applicationContext, metadataReaderFactory); actions.setBasePackages(Collections.singletonList(“com.infopro.synergy.web”));
return actions;
Hi CK,
Thanks. Honestly, I thought that setting an upper-level package would just slow down the search, but produced the same results. So please let us know if you find the reason.
Regards,
Konstantin