v4.0 Migration Guide
Application Structure
- Change project structure to a Spring project structure:
- [application-name] -> Project files (pom.xml, package.json...) >
- src
- main
- java -> ApplicationBoot + Java classes
* **resources** -> Properties
>
- application/[application-name] -> XML files
* config -> AWE Properties overwritten
* js -> Javascript files
* css -> CSS files
* less -> LESS files
* schemas -> XSD Schemas
* static -> Images/Fonts
* webpack -> Webpack configuration
* sql -> SQL Initialization files
* test
>
- java -> JUnit Tests
* *resources* -> Test properties
* *selenium* -> Selenium suites
Maven
- Change POM file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.almis.awe</groupId>
<artifactId>awe-starter-parent</artifactId>
<version>4.0.7</version>
<relativePath/>
</parent>
<artifactId>[project-name]</artifactId>
<groupId>[project-group]</groupId>
<version>[project-version]</version>
<name>[Project name]</name>
<description>[Project description]</description>
<properties>
<application.acronym>[project-acronym]</application.acronym>
<start-class>[project-group].AppBootApplication</start-class>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.build.frontend>${project.build.directory}/classes/static/</project.build.frontend>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- AWE -->
<dependency>
<groupId>com.almis.awe</groupId>
<artifactId>awe-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.almis.awe</groupId>
<artifactId>awe-client-angular</artifactId>
</dependency>
<!-- JDBC Drivers (ADD ONLY WHAT YOU NEED) -->
<!-- ORACLE -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
<scope>runtime</scope>
</dependency>
<!-- SQL SERVER -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>4.0.0</version>
<scope>runtime</scope>
</dependency>
<!-- HSQL -->
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.3</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<finalName>[project-name]</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<!-- Copy static files -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>generate-resources</phase>
<id>unpack awe-generic-screens</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>com.almis.awe</includeGroupIds>
<includeArtifactIds>awe-generic-screens</includeArtifactIds>
<includes>schemas/**,docs/**</includes>
<outputDirectory>${project.build.frontend}</outputDirectory>
</configuration>
</execution>
<execution>
<phase>generate-resources</phase>
<id>unpack awe-client-angular</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>com.almis.awe</includeGroupIds>
<includeArtifactIds>awe-client-angular</includeArtifactIds>
<includes>images/**,fonts/**,js/**,css/**,less/**</includes>
<outputDirectory>${project.build.frontend}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- Copy images -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>copy-images</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/classes/static/images/</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources/images/</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<!-- Spring boot -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
<!-- Frontend generation -->
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>install node and yarn</id>
<goals>
<goal>install-node-and-yarn</goal>
</goals>
<configuration>
<nodeVersion>v6.9.1</nodeVersion>
<yarnVersion>v1.6.0</yarnVersion>
</configuration>
</execution>
<execution>
<id>yarn install</id>
<goals>
<goal>yarn</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>webpack</id>
<goals>
<goal>webpack</goal>
</goals>
<configuration>
<arguments>--output-path "${project.build.frontend}"</arguments>
</configuration>
</execution>
</executions>
</plugin>
<!-- Xml validation -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>xml-maven-plugin</artifactId>
<executions>
<execution>
<id>validate</id>
<phase>compile</phase>
<goals>
<goal>validate</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Build an executable JAR -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>${start-class}</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
Javascript & CSS generation
- Define a webpack file to generate custom javascript and css
XML files
Screens
Regular expressions
Dependency
source-type="action"
is not needed anymore. Just add your dependency actions and they will be launched on valid conditions.source-type="none"
andtarget-type="none"
attributes are default values, so you don't need to set them.Remove
source-type="action"
,source-type="none"
andtarget-type="none"
:
^(.*)source\-type\s*=\s*["']action["']\s*(\S+.*)$ => $1$2
^(.*)source\-type\s*=\s*["']none["']\s*(\S+.*)$ => $1$2
^(.*)target\-type\s*=\s*["']none["']\s*(\S+.*)$ => $1$2
Note:
XXXX => YYYY
means that you must search forXXXX
expression and replace withYYYY
expression
Direct replacement:
Fix dependency conditions:
condition="lte" => condition="le"
condition="gte" => condition="ge"
- Grid pagination to grid managed pagination:
pagination="true" => managed-pagination="true"
control-empty-cancel
action tocontrol-unique-cancel
action:
control-empty-cancel => control-unique-cancel
Query & Maintain
Regular expressions
Fix query and maintain filters:
^(\s*<filter.*\s+)value(.*/>.*)$ => $1left-variable$2 -> Replace by left-variable and add the variable name
^(\s*<filter.*\s+)variable(.*/>.*)$ => $1right-variable$2
^(\s*<filter.*\s+)counterfield(.*/>.*)$ => $1right-field$2
^(\s*<filter.*\s+)countertable(.*/>.*)$ => $1right-table$2
^(\s*<filter.*\s+)field(.*/>.*)$ => $1left-field$2
^(\s*<filter.*\s+)table(.*/>.*)$ => $1left-table$2
^(\s*<)field(.*\s+value.*/>.*)$ => $1constant$2
Direct replacement:
Fix query conditions:
condition="=" => condition="eq"
condition="!=" => condition="ne"
condition="LIKE" => condition="like"
condition="NOT LIKE" => condition="not like"
condition="&gt;" => condition="gt"
condition="&gt;=" => condition="ge"
condition="&lt;" => condition="lt"
condition="&lt;=" => condition="le"
condition="IS NULL" => condition="is null"
condition="IS NOT NULL" => condition="is not null"
condition="IN" => condition="in"
condition="NOT IN" => condition="not in"
- Variable identifiers can't be used in field aliases.
- CASE and CONCAT definitions must be defined now with the new AWE
<case>
and<operation>
tags. See more at Query Definitions. - Static values must be defined as
<constant>
tags - AWE now offers more flexibility generating queries and filters:
<query id="testRowNumber">
<table id="ope"/>
<field id="l1_nom" alias="name"/>
<over alias="rowNumber">
<field function="ROW_NUMBER"/>
</over>
<order-by field="l1_nom" type="ASC"/>
</query>
<query id="testCoalesce">
<table id="ope"/>
<field id="l1_nom" alias="name"/>
<operation operator="COALESCE" alias="nameNotNull">
<field id="l1_trt"/>
<constant type="NULL"/>
<field id="l1_nom"/>
</operation>
<where>
<filter condition="eq" ignorecase="true">
<left-operand>
<field id="l1_nom"/>
</left-operand>
<right-operand>
<constant value="test"/>
</right-operand>
</filter>
</where>
<order-by field="l1_nom" type="ASC"/>
</query>
<query id="testCaseWhenElse">
<table id="AweThm"/>
<case alias="value">
<when left-field="Nam" condition="eq" right-variable="sunset">
<then>
<constant value="1" type="INTEGER"/>
</then>
</when>
<when left-field="Nam" condition="eq" right-variable="sunny">
<then>
<constant value="2" type="INTEGER"/>
</then>
</when>
<when left-field="Nam" condition="eq" right-variable="purple-hills">
<then>
<constant value="3" type="INTEGER"/>
</then>
</when>
<else>
<constant value="0" type="INTEGER"/>
</else>
</case>
<case alias="label">
<when condition="eq">
<left-operand>
<field id="Nam"/>
</left-operand>
<right-operand>
<constant value="sunset"/>
</right-operand>
<then>
<constant value="SUNSET"/>
</then>
</when>
<when condition="eq">
<left-operand>
<field id="Nam"/>
</left-operand>
<right-operand>
<constant value="sunny"/>
</right-operand>
<then>
<constant value="SUNNY"/>
</then>
</when>
<when condition="eq">
<left-operand>
<field id="Nam"/>
</left-operand>
<right-operand>
<constant value="purple-hills"/>
</right-operand>
<then>
<constant value="PURPLE-HILLS"/>
</then>
</when>
<else>
<constant value="other"/>
</else>
</case>
<order-by field="Nam" type="ASC" nulls="FIRST"/>
</query>
<query id="TestFieldDateFunctions">
<table id="ope" alias="awe"/>
<field id="dat_mod" table="awe" alias="year" function="YEAR"/>
<field id="dat_mod" table="awe" alias="month" function="MONTH"/>
<field id="dat_mod" alias="day" function="DAY"/>
<field id="dat_mod" alias="hour" function="HOUR"/>
<field id="dat_mod" alias="minute" function="MINUTE"/>
<field id="dat_mod" alias="second" function="SECOND"/>
<where>
<filter left-field="l1_nom" condition="eq" ignorecase="true">
<right-operand>
<constant value="test"/>
</right-operand>
</filter>
</where>
<order-by field="dat_mod" table="awe" function="YEAR"/>
</query>
Services
- Web services calls have been changed to microservices calls:
Examples:
<service id="simpleGETMicroservice">
<microservice name="alu-microservice" method="GET" endpoint="/invoke" content-type="JSON"/>
</service>
<service id="simpleGETMicroservice2">
<microservice name="alu-microservice2" method="GET" endpoint="/invoke" content-type="JSON"/>
</service>
<service id="simpleGETMicroserviceWithWrapper">
<microservice name="alu-microservice" method="GET" endpoint="/invoke"
wrapper="com.almis.awe.service.dto.ServiceDataWrapper" content-type="JSON"/>
</service>
<service id="simpleGETMicroserviceWithParameter">
<microservice name="alu-microservice" method="GET" endpoint="/invoke" content-type="JSON">
<service-parameter name="param1" type="STRING"/>
</microservice>
</service>
<service id="simpleGETMicroserviceWithWildcard">
<microservice name="alu-microservice" method="GET" endpoint="/invoke/{param1}" content-type="JSON">
<service-parameter name="param1" type="STRING"/>
</microservice>
</service>
<service id="simpleGETMicroserviceWithWildcardAndParameter">
<microservice name="alu-microservice" method="GET" endpoint="/invoke/{param1}" content-type="JSON">
<service-parameter name="param1" type="STRING"/>
<service-parameter name="param2" type="STRING"/>
</microservice>
</service>
<service id="simplePOSTMicroserviceWithParameters">
<microservice name="alu-microservice" method="POST" endpoint="/invoke" content-type="JSON">
<service-parameter name="param1" type="STRING"/>
<service-parameter name="param2" type="STRING"/>
</microservice>
</service>
<service id="simplePUTMicroserviceWithParameters">
<microservice name="alu-microservice" method="PUT" endpoint="/invoke" content-type="JSON">
<service-parameter name="param1" type="STRING"/>
<service-parameter name="param2" type="STRING"/>
</microservice>
</service>
<service id="simpleDELETEMicroserviceWithWildcard">
<microservice name="alu-microservice" method="DELETE" endpoint="/invoke/{param1}" content-type="JSON">
<service-parameter name="param1" type="STRING"/>
</microservice>
</service>
Locales
- Rename Local-XX.xml files to Locale-XX.xml
- Direct replacement:
<locals => <locales
</locals> => </locales>
<local => <locale
</local> => </locale>
Properties
Encode again the properties encoded with ENC(xxxx)
at the Encrypt util screen.
Java files
Package refactorization
- Direct replacement:
- Packages
com.almis.awe.core.services.data.global.XMLWrapper => com.almis.awe.model.entities.XMLFile
com.almis.awe.core.services.data.global.XMLElement => com.almis.awe.model.entities.XMLFile
com.almis.awe.core.services.data.service.ServiceData => com.almis.awe.model.dto.ServiceData
com.almis.awe.core.services.controller.DataController => com.almis.awe.service.QueryService
com.almis.awe.core.services.controller.MaintainController => com.almis.awe.service.MaintainService
com.almis.awe.core.util.DateUtil => com.almis.awe.model.util.data.DateUtil
com.almis.awe.core.beans.ComponentAddress => com.almis.awe.model.entities.actions.ComponentAddress
com.almis.awe.core.services.data.action.ClientAction => com.almis.awe.model.entities.actions.ClientAction
com.almis.awe.dto => com.almis.awe.model.dto
com.almis.awe.core.services.data.global => com.almis.awe.model.dto
com.almis.awe.type => com.almis.awe.model.type
com.almis.awe.core.services.data.query => com.almis.awe.model.entities.queries
com.almis.awe.core.services.data.maintain => com.almis.awe.model.entities.maintain
com.almis.awe.core.exception => com.almis.awe.exception
XMLElement => XMLWrapper
AWEConstants => AweConstants
AweConstants.PARAMETER_MAX => AweConstants.COMPONENT_MAX
- Logging
- Remove com.almis.awe.core.util.LogUtil
- Import
org.apache.logging.log4j.LogManager
andorg.apache.logging.log4j.Logger
- Create a static logger field:
// Logger
private static Logger logger=LogManager.getLogger(MyClass.class);
- Use static logger. For example:
logger.log(Level.INFO,"[{}] No books defined for this treatment",treatment.getID());
- Alternate logger: Lombok
- Add
@Slf4j
annotation on top of the class:
@Slf4j
public MyClass{
...
}
- Use lombok logger:
log.error("My error message {}",moreInformationInVariables,exception);
AWE packages
- There are two main packages in AWE 4.0:
awe-spring-boot-starter
andawe-model
. - awe-spring-boot-starter is the core package of AWE. AWE based web applications must import this package.
- To call AWE services, autowire services from
com.almis.awe.services
(No more Controller calls) - awe-model is the interface package of AWE. AWE related applications (communication modules, microservices, etc) can import this package to gain access to interface classes.
Java services must be migrated to Spring architecture
- Remove all controllers if they don't do anything
- Class structure in AWE has been changed radically. Check your Java imports
- Move
manager
package toservice
package - Rename all XxxManager.java classes to XxxService.java classes
- Add
@Service
annotation to XxxService.java classes - Use Spring methodology (
@Autowired
constructors,@Value
to retrieve properties, etc) - Extend all XxxService classes from
ServiceConfig
if they are using: com.almis.awe.core.singleton.LocalSingleton
=> Extend fromcom.almis.awe.config.ServiceConfig
and callgetLocale
methodscom.almis.awe.core.singleton.PropertySingleton
=> Extend fromcom.almis.awe.config.ServiceConfig
and callgetProperty
methods (And best of all use@Value
instead ofgetProperty
methods)com.almis.awe.core.services.controller.SessionController
=> Extend fromcom.almis.awe.config.ServiceConfig
and callgetSession
com.almis.awe.core.services.data.global.Context
=> Extend fromcom.almis.awe.config.ServiceConfig
and callgetRequest
to retrieve request parameters- Remove also ContextUtil.getContext() access
- Use also
getRequest().getTargetAction()
to retrieve the action target called. - More information on Locale retrieval, Property retrieval , Session retrieval and Request retrieval.
- Adapt custom authentication if overwritten in application
- Use
QueryService
instead ofDataController
. AlllaunchQuery
methods now returnServiceData
beans instead ofDataList
. You can retrieve theDataList
withserviceData.getDataList()
method. - Use
MaintainService
instead ofMaintainController
.
Locale retrieval
- Extending from
ServiceConfig
you get access togetLocale
methods:
getLocale("ERROR_TITLE_LAUNCHING_MAINTAIN");
- You can pass variables to replace on locale simply by adding them as arguments:
getLocale("ERROR_TITLE_LAUNCHING_MAINTAIN",treatment.getID(),task.getID());
Property retrieval
- Extending from
ServiceConfig
you get access togetProperty
methods:
getProperty("var.trt.thd.sug.tim",100);
- Anyway it's more legible and faster to retrieve properties the Spring way:
@Value("${var.trt.thd.sug.tim:100}")
private Integer suggestTime;
Session retrieval
- Extending from
ServiceConfig
you get access togetSession
methods:
getSession().getParameter(AweConstants.SESSION_DATABASE);
Request retrieval
- Extending from
ServiceConfig
you get access togetRequest
methods instead of retrieving them fromContext
:
getRequest().getParameter(AweConstants.PARAMETER_MAX).textValue();
o
getRequest().getParameterAsString(AweConstants.PARAMETER_MAX);
- You can also add some variables to the request:
getRequest().setParameter("someList",someList);
Datalist type
- DataList
getRows
method has changed its' signature fromArrayList<HashMap<String, CellData>>
to a more generic signature:List<Map<String, CellData>>
.
Beans
- Use copy constructor instead of Cloneable interface:
public class MyClass implements Copyable<MyClass> {
private String myProp1;
private String myProp2;
/**
* Default constructor
*/
public MyClass() {
}
/**
* Copy constructor
*/
public MyClass(MyClass other) {
this.myProp1 = other.myProp1;
this.myProp2 = other.myProp2;
}
/**
* Copy method
* @return Copy of this object
*/
public MyClass copy() {
return new MyClass(this);
}
}
or use Lombok:
@Data
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class MyClass {
private String myProp1;
private String myProp2;
}
... and clone it with the builder:
MyClass myNewClass=myOldClass.toBuilder().build();
- Remove from beans all methods which uses any external class. A bean only should have methods which interact over their own fields.
fill
client action now has only one parameter: datalist
, which will contain the full DataList:
serviceData.addClientAction(new ClientAction("fill")
.setAddress(address)
.addParameter("datalist",datalist)
.setAsync(true));
... but it's simpler to use the new ClientAction builders:
serviceData.addClientAction(new FillActionBuilder(address,datalist).setAsync(true).build());
FileData
bean has now a new implementation:
Use FileUtil.fileDataToString
to generate a token:
public String fileDataToString(FileData fileData)
Use FileUtil.stringToFileData
to retrieve a FileData bean from a token:
public FileData stringToFileData(String fileStringEncoded)
Note: FileUtil is an autowireable @Component
Web services ↪️ Microservices
- Adapt web service interface as microservice
Awe has a new service connector layer to make requests to microservices
and rest
services.
See services doc.
Note: To migrate the existing web services in the applications that used with AWE 3
, you have to take into account
the following points:
Parameter sending:
Maintains: The attribute name of
service-parameter
has to be the same as the attributeid
ofvariable
field of serve elements.maintains.xml<target name="MyMaintain">
<serve service="MyService">
<variable id="id1" type="STRING" name="criterion1.selected"/>
<variable id="id2" type="STRING" name="criterion2.selected"/>
<variable id="id3" type="STRING" name="criterion3.selected"/>
</serve>
</target>global/Services.xml<service id="CtrEvnDetIsuCnfSer">
<microservice name="alu-microservice" method="POST" endpoint="/maintain/myMicroservice" content-type="JSON">
<service-parameter name="id1" type="STRING" list="true"/>
<service-parameter name="id2" type="STRING" list="true"/>
<service-parameter name="id3" type="STRING" list="true"/>
</microservice>
</service>webservice/services.xml<service name="myMicroService" type="MAINTAION" call="myWebService">
<param list="false" name="id1" type="STRING"/>
<param list="false" name="id2" type="STRING"/>
<param list="false" name="id3" type="STRING"/>
</service>Queries: The attribute name of
service-parameter
has to be the same as the attributeid
of queryfield
. Usually, you have to add alias field with the name to describe that field.queries.xml<query id="MyQuery" service="MyService">
<field id="id1" alias="alias1"/>
<field id="id2" alias="alias2"/>
<field id="id3" alias="alias3"/>
...
</query>global/services.xml<service id="MyService">
<microservice name="alu-microservice" method="POST" endpoint="/data/myMicroService" content-type="JSON">
<service-parameter list="false" name="id1" type="STRING"/>
<service-parameter list="false" name="id2" type="STRING"/>
<service-parameter list="false" name="id3" type="DATE"/>
</microservice>
</service>webservice/services.xml<service name="myMicroService" type="DATA" call="myWebService">
<param list="false" name="id1" type="STRING"/>
<param list="false" name="id2" type="STRING"/>
<param list="false" name="id3" type="DATE"/>
</service>The number of
service-parameters
must be equal toparam
of service element (webservice).The order of
service-parameters
matters.