# Back-end Development Guide
Version: 1.0 | Release Date: 30/6/2018
# Logical Modules
# Module's CURD
If you are not familiar with the M18 Module, you can refer to Module.
M18 provides basic and common interfaces for Module records to create, update, read and delete easily.
# Use of Web Service
Create Entity
Create a blank entity for desired module.
WsParameter param = WsLib.createWsParam("entity/create/" + getModule(), WsType.post);
param.addQueryParam("menuCode", getMenuCode());
HttpResponse response = WsLib.callWs(param);
if (WsLib.isResponseOK(response)) {
String entryJson = WsLib.resolveResponse(response);
if (!FacesUtil.isEmpty(entryJson)) {
SqlEntity result = JSON.parseObject(entryJson, SqlEntity.class);
}
} else {
WebUtil.showMessageInfo(response, null, "createFail");
}
HTTP Request
POST http://[server]/jsf/rfws/entity/create/[module]?menuCode=[menuCode]
Parameters
Name | Type | Description |
---|---|---|
module | String (Path) | Required. Module name |
menuCode | String (Query) | Required. Menu code |
param | json String (Query) | Used when module's checker needs parameters for special treatment |
Response
Note: If response's result is success, a blank SqlEntity
will be found.
Note: If response's result is fail, CheckMsg
in JSON format can be found in the response's header using the key error_info
.
Type | Location | Description |
---|---|---|
success | Body | Module's blank SqlEntity |
fail | Header(error_info) | CheckMsg in JSON format |
Read Entity
Read module's record in SqlEntity format.
WsParameter param = WsLib.createWsParam("entity/read/" + getModule(), WsType.get);
param.addQueryParam("menuCode", getMenuCode());
param.addQueryParam("id", action.getData(ActionParam.id).toString());
param.addQueryParam("iRev", action.getDataOrDefault(ActionParam.iRev, 0).toString());
HttpResponse response = WsLib.callWs(param);
if (WsLib.isResponseOK(response)) {
String entryJson = WsLib.resolveResponse(response);
if (!FacesUtil.isEmpty(entryJson)) {
entity = JSON.parseObject(entryJson, SqlEntity.class);
}
} else {
WebUtil.showMessageInfo(false, null, null, "readFail");
}
HTTP Request
GET http://[server]/jsf/rfws/entity/create/[module]?menuCode=[menuCode]&id=[id]
Parameter
Name | Type | Description |
---|---|---|
module | String (Path) | Required. Module name |
menuCode | String (Query) | Required. Menu code |
id | long (Query) | Required. Entity's id |
iRev | int (Query) | Version of the record. 0 or leave it blank to read the latest record |
param | json String (Query) | Used when module's checker needs parameters for special treatment |
Response
Note: If response's result is success, the record in SqlEntity
format will be found.
Note: If response's result is fail, CheckMsg
in JSON format can be found in the response's header using the key error_info
.
Type | Location | Description |
---|---|---|
Success | Body | Record in SqlEntity format |
Fail | Header(error_info) | CheckMsg in JSON format |
Save Entity
Save the module's record in SqlEntity format.
WsParameter param = WsLib.createWsParam("entity/save/" + getModule(), WsType.put);
param.addQueryParam("menuCode", getMenuCode());
param.addQueryParam("entity", JSON.toJSONString(getEntity()));
HttpResponse response = WsLib.callWs(param);
if (WsLib.isResponseOK(response)) {
long recordId = Long.parseLong(WsLib.resolveResponse(response));
action.setData(ActionParam.id, recordId);
}
WebUtil.showMessageInfo(response, "saveSuccess", "saveFail");
HTTP Request
PUT http://[server]/jsf/rfws/entity/save/[module]?menuCode=[menuCode]&entity=[SqlEntity json String]
Parameters
Name | Type | Description |
---|---|---|
module | String (Path) | Required. Module name |
menuCode | String (Query) | Required. Menu code |
entity | json String (Query) | Required. SqlEntity in JSON format |
param | json String (Query) | Used when module's checker needs parameters for special treatment |
Response
Note: If response's result is success, id
of the saved SqlEntity
will be returned.
Note: If response's result is fail, CheckMsg
in JSON format can be found in the response's header using the key error_info
.
Type | Location | Description |
---|---|---|
Success | Body | id of SqlEntity |
Fail | Header(error_info) | CheckMsg in JSON format |
Delete Entity
Delete module's record using SqlEntity format.
WsParameter param = WsLib.createWsParam("entity/delete/" + getModule(), WsType.delete);
param.addQueryParam("menuCode", getMenuCode());
param.addQueryParam("id", action.getData(ActionParam.id).toString());
HttpResponse response = WsLib.callWs(param);
WebUtil.showMessageInfo(response, "deleteSuccess", "deleteFail");
HTTP Request
DELETE http://[server]/jsf/rfws/entity/delete/[module]?menuCode=[menuCode]&id=[id]
Parameters
Name | Type | Description |
---|---|---|
module | String (Path) | Required. Module name |
menuCode | String (Query) | Required. Menu code |
id | long (Query) | Required. id of the ready-to-delete record. |
param | json String (Query) | Used when module's checker needs parameters for special treatment |
Response
If response's result is fail, CheckMsg
in JSON format can be found in the response's header using the key error_info
.
Type | Location | Description |
---|---|---|
Success | Status | Status = 200 means success |
Fail | Header(error_info) | CheckMsg in JSON format |
# Use of EJB
Create Entity
Different from web service, cache of the record will not be used when using EJB.
@EJB
CawEntityCurdAction curdEJB;
public void create(String module,string menuCode){
SeCreateParam param = new SeCreateParam(module);
param.setMenuCode(menuCode);
EntityResult result = curdEJB.createEntity(param);
}
SeCreateParam
Input: Module name and menu code.
If module's checker needs parameters for special treatment, user can put those variables into SeCreateParam
's jsonParam
.
Return Type
Note: If SqlEntity
in EntityResult
is not null
, a blank SqlEntity
can be retrieved.
Note: If SqlEntity
in EntityResult
is null
, error messages can be found under List<CheckMsg>
.
Read Entity
Different from web service, cache of the record will not be used when using EJB.
@EJB
CawEntityCurdAction curdEJB;
public void read(String module, String menuCode, long id, int iRev){
SeReadParam param = new SeReadParam(module);
param.setMenuCode(menuCode);
param.setEntityId(id);
param.setIrev(iRev);
EntityResult result = curdEJB.loadEntity(param);
}
SeReadParam
Input: Module name, menu code, id of the record. Optional: iRev
to read specific version.
If module's checker needs parameters for special treatment, user can put those variables into SeReadParam
's jsonParam
.
Return Type
Note: If SqlEntity
in EntityResult
is not null
, record of the specific version can be retrieved.
Note: If SqlEntity
in EntityResult
is null
, error messages can be found under List<CheckMsg>
.
Save Entity
@EJB
CawEntityCurdAction curdEJB;
public void save(String module, String menuCode, SqlEntity entity){
SeSaveParam param = new SeSaveParam(module);
param.setMenuCode(menuCode);
param.setSqlEntity(entity);
param.setInsertMir(true);
CheckResult result = curdEJB.updateEntity(param);
}
SeSaveParam
Input: Module name, menu code, record's in SqlEntity format. Optional: Set insertMir
to true
if historical record needs to be kept.
If module's checker needs parameters for special treatment, user can put those variables into SeSaveParam
's jsonParam
.
Return Type
Note: If pass
in CheckResult
is true, the record has been saved successfully, id
can be found using the method getEntityId().
Note: If pass
in CheckResult
is false, error messages can be found under List<CheckMsg>
.
Delete Entity
@EJB
CawEntityCurdAction curdEJB;
public void delete(String module, String menuCode, long id){
SeDeleteParam param = new SeDeleteParam(module);
param.setMenuCode(menuCode);
param.setEntityId(id);
param.setInsertMir(true);
CheckResult result = curdEJB.deleteEntity(param);
}
SeDeleteParam
Input: Module name, menu code, id of the record. Optional: Set insertMir
to true
if historical record needs to be kept.
If module's checker needs parameters for special treatment, user can put those variables into SeDeleteParam
's jsonParam
.
Return Type
Note: If pass
in CheckResult
is true, the record has been deleted successfully.
Note: If pass
in CheckResult
is false, error messages can be found under List<CheckMsg>
.
# Checker and Transaction
During the process of module CURD, checker, defined in module.xml, can be used to handle some checking or process special treatments of the records.
Checker's method setting
Define checker in module.xml. The method with @EntityCheck annotation in checker files will be used during CURD process. Parameter: SeCurdParam
public class EmployeeChecker {
@EntityCheck(range = CheckRange.BEFORE, type = CheckType.SAVE)
public CheckMsg checkMothed(SeCurdParam param) {
return null;
}
@EntityCheck's Parameters'
Name | Required | Type | Description |
---|---|---|---|
type | true | CheckType | Options: SAVE(1), DELETE(2), CREATE(3), READ(4) |
range | true | CheckRange | Options: BEFORE(1), AFTER(2), BOTH(3), SAVING(4), ROLLBACK(5), DELETEING(6). SAVING can only be used when CheckType = SAVE . DELETEING can only be used when CheckType = DELETE |
checkOrder | false | int | Default: 0. The smaller the number, the earlier it will process |
actionBreaker | false | boolean | Default: false. If true and checker's method return errors, the process will stop immediately and return the error message(s) |
overrideMethod | false | String | Use Class.method to override methods in order checker |
Save Process
curdEJB invokes save method.
Checker Save-Before: Invoke the method with CheckType = SAVE, CheckRange = BEFORE or BOTH. If any method returns CheckMsg with pass = false, the save process will be stopped and return error message. The purpose of these methods are validating data. It does not involve any data change in database as it is still not in the save transaction.
Sql Before: If no error occurs during the Checker Save-Before stage, it will create a save transaction and run the SQL retrieved from
SeSaveParam.getSqlBeforeDtos()
. If error occurs during this stage, the save transaction will be stopped and rolled back. Methods with CheckType = SAVE, CheckRange = ROLLBACK will be invoked. Error message will be returned.Update To Database: If no error occurs during the Sql Before stage, entity will be saved into the database. If error occurs during this stage, the save transaction will be stopped and rolled back. Methods with CheckType = SAVE, CheckRange = ROLLBACK will be invoked. Error message will be returned.
Checker Saving: After saving the entity into the database, methods with CheckType = SAVE, CheckRange = SAVING will be invoked. If error occurs during this stage, the save transaction will be stopped and rolled back. Methods with CheckType = SAVE,CheckRange = ROLLBACK will be invoked. Error message will be returned.
Sql After: If no errors return during the Checker Saving stage, it will run the SQL retrieved from
SeSaveParam.getSqlAfterDtos()
. If error occurs during this stage, the save transaction will be stopped and rolled back. Methods with CheckType = SAVE, CheckRange = ROLLBACK will be invoked. Error message will be returned.If no error occurs during the process, the save transaction will be committed.
Checker Save-After: Methods with CheckType = SAVE, CheckRange = AFTER or CheckRange = BOTH will be invoked. If error returns during the process, the save transaction will NOT be rolled back. It is not recommended to do any error checking during this process.
Please refer to below diagram for more details.
Delete Process
Almost same as Save Process, the only difference is the method with CheckType = DELETEING will be invoked before the Entity is deleted.
Create Process
There are no Sql before, Saving, Sql After stages in Create Process when compared with Save Process.
Read Process There are no Sql before, Saving, Sql After stages in Read Process when compared with Save Process.
Search Module Setup a stSearch with code same as the module name in stSearch.xml.
# Non-Module's Operations
M18 has provided SqlTableCurdEAO
to handle the operation of non-module's tables.
Note: If the target table belongs to any module, it is not recommended to use this method.
Read Record
Use read(StReadParam param)
in SqlTableCurdEAO
to read the record in database.
@EJB
protected SqlTableCurdEAO curdEAO;
public void read(long id){
StReadParam param = new StReadParam();
param.setTableName("draft");
param.setId(id);
SqlTable data = curdEAO.read(param);
}
Create blank table and save data
Use genEmptyTable(String tableName)
to retrieve a blank SqlTable
with correct format.
Use save(StUpdateParam param)
in SqlTableCurdEAO
to save the table's data.
tableName and SqlTable must be put into StUpdateParam
.
@EJB
protected SqlTableCurdEAO curdEAO;
public long save(DraftDto draft){
SqlTable sqlTable = emptyTable.genEmptyTable("draft");
sqlTable.addRow();
sqlTable.setBoolean(1, "autoSave", draft.isAutoSave());
sqlTable.setString(1, "draftCode", draft.getDraftCode());
sqlTable.setObject(1, "lastModifyDate", new Date());
***//do some coding
StUpdateParam param = new StUpdateParam();
param.setTableName("draft");
param.setData(sqlTable);
try {
long insertId = curdEAO.save(param);
return insertId;
} catch (SQLException e) {
CawLog.logException(e);
}
reutrn 0;
}
Delete record
Use delete(StDeleteParam param)
in SqlTableCurdEAO
to delete the record in database.
@EJB
protected SqlTableCurdEAO curdEAO;
public void delete(){
StDeleteParam param = new StDeleteParam();
param.setTableName("draft");
param.setId(1);
try {
curdEAO.delete(param);
} catch (SQLException e) {
CawLog.logException(e);
}
}
# Core XML Configuration
With reference to Logical Modules, M18 uses XML structure to handle common operations and functions. Besides logical modules, many features and functions use XML to control and handle.
The XML's default location is under share project. The path is like /main/resources/META-INF/*.xml
# Keywords in Table
As almost all tables are defined in XML format, basic requirement and limitation in XML should be considered.
Although XML is not mainly used in M18 server, XML affects most of the key operation when starting JBoss. It is suggested it should be modified cautiously.
It is recommended to define the code in small letter. Users must not use any special character.
It is also suggested to use app name as the prefix of table and module name. A good naming rule usually prevents conflicts among different parties, escpecially under M18 App concept.
# datadict.xml
datadict.xml is used to describe the SqlTable, view and its column's information in MySQL database. It also defines the table constrains like index, foreign key, unique key, etc. M18 transfers the information in datadict.xml and creates those tables and constrains in the database. It is suggested to avoid using any keywords in MySQL.
Note: When M18 is started, M18 will compare the current database table structures and constrains with xml and will only add/change those that are different.
Note: Synchronization of view, stored procedure and function will be explained later
Table's attributes
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
name | Y | String | Define the name of the table, unique in M18, small letter only and must not use any special characters | |
sys | N | Boolean | false | Indicate if this table belongs to system. System tables will not check access right and cannot be imported not exported |
mess | Y | String | Define the mess code of the table | |
pk | N | String | id | Define the primary key of this table. Must be id |
virtual | N | Boolean | false | Indicate if the current table's information, including columns or constrains, will not affect the MySQL database |
supportUdf | N | Boolean | true | Indicate if UDF column will be supported |
templet | N | Boolean | false | Indicate if the current table is a template. A template will not affect MySQL database and generate cache. Other table can use <Inherit> to inherit the information under this template |
extend | N | Boolean | false | Indicate if the current table is the extension of any existing records |
onlyInMain | N | Boolean | false | Indicate if xxx_v table will be created |
Inherit's attributes
Name | Type | Description |
---|---|---|
name | String | Define the name of the table in which template = true. |
Note: If column's information which belongs to template needs to be modified in the current table, add that column in the table and change the attribute accordingly. However, this method is not recommended as this will be mis-leading.
Column's attributes
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
name | Y | String | Define the column name, unique key under the same table. Lower camel case naming rule is recommended. MUST NOT USE SPECIAL CHARACTERS e.g. *, _, - etc. | |
type | Y | String | Define the type of the column | |
mess | Y | String | Define the column's messCode | |
length | N | Integer | Define the data length. Used when type = char/number | |
decimal | N | Integer | Define the decimal length. Used when type = number | |
defValue | N | String | Define the default value of the column | |
defPattern | N | String | Define the pattern, defined in Pattern | |
required | N | Boolean | false | Indicate if the column is required during saving |
identity | N | Boolean | false | Indicate if auto increment should be enabled for this column. Usually for id column |
i18nField | N | Boolean | false | Indicate if multiple language is supported for this field. Used only when type = char |
i18nSrc | N | String | Define the column name (Table.Column) that i18n field will be enabled together. Ignore when i18nField = false | |
allowNull | N | Boolean | false | Indicate if the column allows null value in database. Note: Some type of columns in Database require null value |
buildIn | N | Boolean | false | Indicate if the column is build-in. Some functions like fields in search will group these build-in fields into a folder |
dataImport | N | Boolean | true | Indicate if the column can be imported |
dataExport | N | Boolean | true | Indicate if the column can be exported |
skipLookup | N | Boolean | false | Indicate if the column will not be shown when lookup |
skipAccess | N | Boolean | false | Indicate if the column needs to be considered when access right is enabled |
checkLookupVal | N | Boolean | false | Indicate if the column needs to search again for access right when being imported |
genRuleDate | N | Boolean | true | Indicate if the column can be used in generating date field in CodeFormat setup |
batchUpdate | N | Boolean | true | Indicate if the column can be batch-updated. Note: It is not recommended to batch-update the fields which need calculation |
udfUpdate | N | Boolean | Indicate if the column can be used in UDF Update | |
skipFLR | N | Boolean | false | Indicate if the column is controlled by field right |
dataDupCheck | N | Boolean | false | Indicate if the column can be used in Duplicated Data Check.If the value is not set, the column can be used in Duplicated Data Check when the column type is string or date , or the pattern is lookup. |
dataEasy | N | Boolean | dataImport | Indicate if the column can be used in Data Easy. If the value is not set, the value default use dataImport value. |
lookupCurrentModule | N | Boolean | false | Indicate if the column is lookup current module. |
Column type's description
Name | Description |
---|---|
int_unsigned | Java: long. Note: All id should use int_unsigned |
bigint | Java: long. Not recommended to use. |
bit | Java: boolean. |
datetime | Java: date. Date with time. |
date | Java: date. |
nvarchar/varchar | Java: String. length required. |
text/longtext | Java: String. Note: text's length is limited. Use longtext if text's length is not enough. |
numeric | Java: double. length and decimal required. |
Note: File type please refer to File Stream Handling |
Index's attributes'
Name | Type | Description |
---|---|---|
name | String | Define the index's name. Unique in the same table. |
columns | String | Define the column(s) used in index. ; separated. |
unique | Boolean | Indicate if this is a unique key. |
fk's attributes'
Name | Type | Description |
---|---|---|
name | String | Define the name of Foreign Key. |
columns | String | Define the column name in the current table. |
refTable | String | Define the target table. |
refColumns | String | Define the target column in target table. Default: id . |
Note: M18 will create or update the tables/columns/constrains information from datadict.xml to MySQL Database. M18 will not delete any columns from the database, it is recommended to handle it manually.
It is recommended to create another field if the attribute of any field needs modification because there may be unexpected problem when datadict.xml starts synchronizing the database.
Note: M18 allows modification of attribute in columns. However, there will not be any recovery nor warning message if error occurred.
Please pay attention when the field's length is shortened as database may cut the data based on the new length and it is unrecoverable.
Example
<?xml version="1.0"?>
<dd xmlns="http://www.multiable.com/datadict">
<!-- test info -->
<table name="test_base" mess="test_base" templet="true">
<column name="id" type="int_unsigned" mess="core.id" identity="true"/>
<column name="iRev" type="int" mess="core.iRev" defValue="0" defPattern="iRev" dataImport="false"/>
<column name="createDate" type="datetime" mess="core.createDate" defValue="NOW()" defPattern="datetime" dataImport="false" dataExport="false" buildin="true"/>
</table>
<table name="test" mess="test" pk="id">
<inherit name="test_base"/>
<column name="code" type="varchar" mess="code" length="100" defPattern="code" required="true"/>
<column name="desc" type="varchar" mess="desc" length="200" defPattern="desc" i18nField="true"/>
<index name="code" columns="code" unique="true"/>
</table>
</dd>
# Database Synchronization
When M18 is started, besides datadict.xml, it will also update other information like views, stored procedures, functions, etc.
How M18 update these information:
It is required to create three folders with name view
, proc
and func
under the path ejb/src/main/resources/sql/. Every views, stored procedures, functions, etc should have its own file and the content should start with deleting the information and be followed by creating it again.
Example:Stored Procedure's file.
drop procedure if exists test_dm;
DELIMITER $$
CREATE PROCEDURE test_dm(IN dbname varchar(100))
BEGIN
select * from test A where A.db = dbname;
END$$
DELIMITER ;
Update process is like below:
# pattern.xml
M18 uses pattern to describe how a field looks like in database and in user interface.
record's attributes
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
code | true | string | Define the name. Unique key | |
type | true | string | Define the pattern's type. Options: text , number , date , options , boolean , lookup , color | |
info | false | string | Define the description |
text's attributes. If pattern's
type
=text
,text
tag will be used in the next level to denote it is of text type.
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
length | true | int | Define the length of the text | |
trim | false | boolean | false | Indicate if the space in front of or behind the word will be removed |
upperCase | false | boolean | false | Indicate if the words should be changed to capital letter |
mask | false | string | Define the format | |
regex | false | string | Define the regular expression | |
html | false | boolean | false | Indicate if the user interface should use html component |
number's attributes. If pattern's
type
=number
,number
tag will be used in the next level to denote it is of numeric type.
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
length | true | int | Define the total length (integer + decimal) of the number | |
decimal | false | int | 0 | Define the length of the decimal part |
max | false | double | false | Define the maximum value of the number |
min | false | double | Define the minimum value of the number | |
noSep | false | boolean | false | Indicate if the number should be separated by thousand separator (, ) in user interface |
negativeSymbol | false | boolean | false | Indicate if blanket will be used if the value is negative in user interface |
lookup's attributes. If pattern's
type
=lookup
,lookup
tag will be used in the next level to denote it is of lookup type.
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
searchType | true | string | Define the stSearch used for this lookup |
options's attributes. If pattern's
type
=options
,options
tag will be used to denote it is a drop down list.
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
valueClass | true | string | string | Define the type of the option's value. Options: String , Integer , Double , Combobox |
emptyValue | false | string | Define the value if no option has been chosen |
option's attributes
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
value | true | string | Define the value when the option is selected | |
label | true | string | Define the value displayed in the user interface |
option's icon attributes
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
name | false | String | Icon name in the library. | |
library | false | String | Library url in system. | |
url | false | String | The icon url. | |
style | false | String | Icon css style. | |
styleClass | false | String | Icon css style class. |
Special Patterns Followings are some special patterns used by M18. It is not suggested to modify it.
<!-- this is for file.info the id of filedata -->
<record code="fileId" type="number"><number length="12" /></record>
<!-- this is for image, use code for safe! -->
<record code="imgCode" type="text"><text length="60" /></record>
<!-- date with time -->
<record code="datetime" type="date"></record>
<!-- time in format HH:MM-->
<record code="time" type="text"><text length="10" /></record>
<!-- date only-->
<record code="date" type="date"></record>
<!-- color component-->
<record code="color" type="color"></record>
<!-- can only save with length less than 2^32. Use longtext if not enought. -->
<record code="charMax" type="text"><text length="-1" /></record>
<!-- json string-->
<record code="json" type="text"><text length="-1" /></record>
<!-- unlimited length. datadict.xml should use type = longtext for this pattern-->
<record code="longtext" type="text"><text length="-1" /></record>
<!-- html component-->
<record code="html" type="text"><text length="-1" html="true" /></record>
# navmenu.xml
M18 uses navmenu.xml to define the system's Menu displayed in user interface. User can assign access right through this menu.
menuType description. menuType defines the type of the menu with the use of icons and images.
Name | Child Element | Description |
---|---|---|
name | Define a menu type. | |
desc | Define the mess code of menu type. | |
icon | name | Define the icon name. (icon used in menu) |
icon | library | Define the location of the icon. System will find the specific image through the library and name |
image | name | Define the image name. (image used in menu) |
image | library | Define the location of the image |
M18 has some default menu type as follows:
FM is used to represent
File Master Module
TRAN is used to represent
Transaction Module
SETTING is used to represent
Setting Module
EBI is used to represent
EBI Module
OTHER/
blank
is used to representOther Module
Example
<menuType name="FM" desc="fm/master">
<icon name="cicon-fm-master-data" library="font/cawIcon"></icon>
<image name="fmIcon.png" library="img/icon"></image>
</menuType>
function's attributes. function defines the available function/ right of a menu. It is also the basic unit of menu's access right.
Name | Required | Description |
---|---|---|
name | Y | Define the function name. Recommended to use alphabet and underscore |
group | N | Define the group shown in [Role Right Setup] |
messCode | Y | Define the messCode used in user interface |
tooltip | Y | Define the tooltip displayed in [Role Right Setup] |
Please refer to [Role Right Setup] for the list of current function.
menuHelper's attributes. Mainly used to create menuParam.
Name | Description |
---|---|
code | Define the menu code that uses the function under the class |
mType | Define the menu type that uses the function under the class |
class | Define the class name that implements MenuHelper. Can use isFor to define specific menu |
folder's attributes
Name | Description |
---|---|
code | Define the folder name |
messCode | Define the folder mess code |
apDebug | Boolean. Only visible in user interface when debug mode is on |
flag | Define the flag in which this flag can be used in java bean |
order | Define the order of the folder in the tree |
menu's attributes
Name | Child Element | Type | Description |
---|---|---|---|
code | String | Define the menu code | |
messCode | String | Define the menu mess code | |
src | String | Define the link when this menu is clicked | |
module | String | Define the module used by this menu | |
mType | String | Define the menu type for this menu | |
templet | Boolean | Indicate if the current menu is a template. Template will not shown in user interface but can be inherited by other menu | |
apDebug | Boolean | Indicate if it can only be visible in user interface when debug mode is on | |
flag | String | Define the flag in which this flag can be used in java bean | |
inherit | name | String | Define the template used |
inherit | include | String | Define the function name only inherit from the template. ; separated |
inherit | except | String | Define the function name exclude from the function list in the template. ; separated |
function | name | String | Define the function name. ; separated |
controller | NA | String | Define the controller class name used for this menu |
listener | NA | String | Define the listener class name used for this menu |
param | key | String | Define the key used in key-value pair for the purpose of adding additional parameters to the editor |
param | value | String | Define the value used in key-value pair for the purpose of adding additional parameters to the editor |
menuParam | name | String | Parameter's name |
menuParam | description | String | Parameter's mess code |
menuParam | pattern | String | Parameter's pattern |
menuParam | folder | String | Parameter's grouping |
order | int | Define the order of the menu in the folder |
Example
<?xml version="1.0"?>
<nm xmlns="http://www.multiable.com/navmenu" app="caw">
<function name="New" group="Editor" messCode="core.saveNew" tooltip="right.tt.new" />
<function name="Save" group="Editor" messCode="core.save" tooltip="right.tt.save" />
<folder code="sys" messCode="core.sys">
<menu code="test" messCode="test" src="view/module/test" module="test" mType="">
<function name="New;Save" />
</menu>
</folder>
</nm>
How to determine if a user has defined access right?
UserRightInfo info = UserLib.getRight(uid);
FrameRight right = info.getFrameRight(menu,beId);
Note: Function added by other parties should use otherRight
in right
to determine.
# module.xml
Module represent a group of editor (or function)'s tables. In M18, module must have a main table and other tables must be related to the primary key of the main table.
module's attributes'
Name | Type | Description |
---|---|---|
name | String | Define the module name |
mess | String | Define the module mess code |
extend | Boolean | Indicate if the current element is the extension of any existing element. |
mainTable | String | Define the main table used in this module. Note: The main table must exist in the child tag table |
useBe | Boolean | Indicate if the current module should use business entity |
fmshare | String | Define the level of data separation. blank means it is not a file master's editor. Y means it is a file master's editor and the data is based on business entity. N means it is a file master's editor and the data is shared among business entity |
useAccess | Boolean | Indicate if the current module should be controlled by Data Guard |
useAttach | Boolean | Indicate if the current module should enable attachment function |
useApv | Boolean | Indicate if the current module should enable approval function |
useChangenote | Boolean | Indicate if the current module should enable change note function |
useCache | Boolean | Indicate if cache should be used. Default true . Note: If the module is manually cached or cannot be cached, it is recommended to turn this flag to false . To update the cache, please increase the iRev in the table |
useAutoGenCode | Boolean | Indicate if the module can use code format setup. New functions is needed to enable this feature |
genCode_Field | String | Define the field that the output from code format setup will be assigned to. Default code |
genCode_Date | String | Define the date field that is used in code format setup. Default createDate |
fieldDataGuard | Integer | Indicate if the current module should enable Field Data Guard. -1 means disabled. 1 means enabled. 0 means enabled when New function is enabled |
udfLogicSkip | Boolean | Indicate if UDF Logic should be skipped |
supportUdf | Boolean | Indicate if current module should support managing UDF fields |
checkCodeExist | Boolean | Indicate if M18 should automatically check if code already exists in the database and warn the user when code is input in the user interface |
dataSynch | String | Define Data Easy should be enabled for this module. New function should be enabled. Default is enabled |
dataImportConveter | String | Define the class name used for special handling in importation. Please refer to Data Import and Export for more details |
excelGener | String | Define the class name used for special handling in exportation. Please refer to Data Import and Export for more details |
template | Boolean | Mark if current configuration is UDF template or not, which is used in UDF Editor |
tableOrders | String | Define the order of the table for most of the process. ; separated. Note: Main Table defined in mainTable must appear first |
codeDupLevel | String | Define the code's duplicated level. If BE , beId + code will be the unique key in the table. Note: M18 will create unique key constraint in the database when the system is started, please ensure the uniqueness of the code before the constraint is created |
skipExpired | Boolean | Indicate the current module does not enable expired function |
skipSysBC | Boolean | Indicate the current module will not create the code unique index. And will not check the duplicated code between BEs. |
importAllowUpdate | Boolean | Indicate the current module can be update in Data Import. Default is true. |
importThreadMode | Boolean | Indicate the current module can be using multithreading in Data Import. Default is true. |
table's attributes
Name | Type | Description |
---|---|---|
name | String | Define the table name. The table name must be already defined in datadict.xml |
tpl | String | Used by UDF Editor. Mark the actual datadict table used by this UDF editor |
c | Boolean | Indicate if this table supports creation of record. If false , new record will not be created in this table |
r | Boolean | Indicate if this table supports reading of record. If false , system will ignore this table when reading record in the module |
u | Boolean | Indicate if this table supports saving of record. If false , system will ignore this table when saving record in the module |
d | Boolean | Indicate if this table supports deletion of record. If false , system will ignore this table when deleting record in the module |
initRow | Integer | Define the number of row added for this table when the module is initialized |
forceInit | Boolean | Indicate if the number of row should be added if the row of the table after reading from database is less than initRow defined |
hpk | String | Define the fields that used to link with the primary key in main table. Note: No need to input when the current table is main table. Do not create any foreign key for this field |
fkey | String | Define the fields in the footer which can be used to determine the uniqueness of the record |
hfname | String | Mark the upper level footer's info |
hfkey | String | The key field of upper level footer |
order | String | Define a field that is used to order the table. Only affect the read action |
cpnType | String | Define the type of this table. if table , the relationship between main table and this table will be 1:N. Note: If table , the table will support UDF Field |
discriminatorColumn | String | Define a field that is used to discriminate the module used when this table is used by many module |
resetDiscriminatorColumn | Boolean | Indicate if discriminator column should be reset |
dataImport | Boolean | Indicate if importation is supported. Note: If the main table does not support importation, the current module will not support importation too |
dataExport | Boolean | Indicate if exportation is supported. Note: If the main table does not support exportation, the current module will not support exportation too |
columnOrders | String | Define the column order for importation. ; separated |
fieldRightSetting | Boolean | Indicate if the current table should enable Field Data Guard |
compareKey | String | Define the field that is used to determine the difference between the historical data and current data. ; separated |
sfKey | String | The key field to join the current footer and upper level footer |
checker's attributes
Name | Type | Description |
---|---|---|
class | String | Define the class used for this checker |
exclude | String | Define the method excluded from the class . Excluded method will not be triggered. ; separated |
include | String | Define the method included from the class . Only included method will be triggered. ; separated |
skipSuper | Boolean | Indicate if methods in super class should be ignored |
apployTo | String | Define the modules that this checker will be applied to. * means apply to all modules |
param's attributes
Name | Description |
---|---|
key | key of the param. use Module.getParam() to get all the parameters in this module. |
value | value of the param. |
dataImportExtend's attributes
Name | Description |
---|---|
extendSrc | Indicate the extend page path which use in Date Import. |
dtoClass | The data class which use in extend page. |
dataExportExtend's attributes
Name | Description |
---|---|
extendSrc | Indicate the extend page path which use in Date Export. |
dtoClass | The data class which use in extend page. |
Example:
<?xml version="1.0"?>
<md xmlns="http://www.multiable.com/module" app="caw">
<module name="user" mess="user" mainTable="user" useAccess="true" useAttach="true">
<table name="user" key="code" initRow="1"/>
<table name="useroption" key="id" initRow="1" forceInit="true"/>
<table name="userrole" key="beId" hpk="hId" cpnType="table"/>
<table name="usercontrol" key="id" initRow="1" hpk="hId" forceInit="true"/>
<table name="userdefbe" key="id" initRow="0" hpk="hId" fKey="beId" order="priority" cpnType="table"/>
<!-- this checker is for all module! -->
<checker class="com.multiable.core.ejb.checker.ModuleChecker" applyTo="*"/>
<checker class="com.multiable.core.ejb.checker.ExtDataChecker" applyTo="*"/>
<checker class="com.multiable.core.ejb.checker.UserChecker"/>
</module>
</md>
# i18n Implementation
M18 supports multiple language display. It is required to display the content at least in English, Simplified Chinese and Traditional Chinese. User can add additional language in M18.
Related document is located in /main/resources/META-INF/lang/Message_*.properties
Developer can use Eclipse to edit the above documents.
Note: For any new mess code, English translation must exist.
To ensure the uniqueness of mess code, it is suggested to use App's name for the prefix of mess code.
Developers can retrieve the translation using the mess code like the following example:
String text = CawGlobal.getMess("EBI");
In addition, if the mess code needs to be used in JavaScript, please add a record in webKey.properties
.
var mText = myView.getMess('EBI');
Note: Please do not use the keyword in HTML if the mess code can be used in JavaScript.
webKey.properties Example
// web mess for core
CloseAllTabs
EBI
all
User defined language and mess code. Please refer to M18 editors: UDF Language, Messcode
M18 supports data saving in multiple language. The simplest way is to set i18nField
of the desired column to true
in datadict.xml. These data will be stored in database in JSON format.
For simplicity, M18 will translate the i18n's fields to the current language. The following is an example showing how desc
, an i18n's field, can be retrieved from SqlTable
.
String desc= employee.getString(1,"desc_en");
employee.setString(1,"desc_zh_CN",desc);
employee.setString(1,"desc_en","");
Note: The multiple language information is stored in the column i18nField
, If you want to modify the i18n's field in database directly, please modify i18nField
as well.
# Search Implementation
Table/ View can be searched by setting up stInfo.xml and stSearch.xml. Lookup component in user interface also searches data using the above two XMLs.
# stInfo.xml
stInfo is to define the table used in searching and to avoid exposing the table directly to the user. If a stInfo is used in stSearch and the corresponding information is not defined in stInfo.xml, the system will generate the corresponding stInfo information according to datadict by default.
stInfo's attributes
Name | Required | Type | Description |
---|---|---|---|
name | true | string | stInfo's name, normally same name as corresponding table |
mess | true | string | stInfo's mess code |
table | true | string | Target table's name (in datadict) |
inCols | false | string | Define which fields in the table are available in this stInfo. If this parameter is empty, all fields representing the corresponding table are available in this stInfo. If not empty, the two fields need to be separated by a semicolon ; |
exCols | false | string | Define which fields in the table are NOT available in the current stInfo. If this parameter is empty, all fields representing the corresponding table are available in this stInfo. If not empty, the two fields need to be separated by a semicolon ; |
autoRelation | false | boolean | Indicate auto create the relation of lookup field |
skipBuildin | false | boolean | Indicate build-in column can used in lookup condition. |
relation's attributes. The relation tag defines how the table of the current stInfo associates with the table of another stInfo.
Name | Required | Type | Description |
---|---|---|---|
col | false | string | The field to join with other's stInfo |
tarSt | true | string | The target stInfo's name |
tarCol | false | string | The target stInfo's column |
tarNullAble | false | boolean | The default is false; if true; the left outer join is used; otherwise, the inner join is used |
joinCond | false | string | If not empty, this joining condition string will be used, otherwise use tarSt and tarCol |
col's attributes. The col tag is used to define a custom field to be returned by the search result. This tag is not required.
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
name | true | string | Field's name | |
mess | true | string | Field's mess code | |
pattern | false | string | Field's pattern | |
sql | false | string | If the value of this field is to be queried from the database, you need to input the SQL query string | |
cond | false | boolean | true | If the value is true, this field can be used as a query condition. |
sort | false | boolean | true | If the value is true, this field can be used for sorting |
show | false | boolean | true | If the value is true, this field can be displayed on the M18's UI. |
handler's attributes. The handler tag is used to configure stlnfo's handler, which can further control the initialization of stInfo. This tag is optional.
Name | Required | Type | Description |
---|---|---|---|
stInfoName | true | string | stInfo's name |
className | true | string | handler's class name |
# stSearch.xml
stSearch's attributes
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
name | true | string | stSearch's name | |
mess | true | string | stSearch's mess code | |
srcSt | false | string | Use the stInfo in stInfo.xml | |
maxLevel | false | int | 3 | Indicate the maximum number of levels that can be related to stInfo |
defCond | false | string | Default query condition | |
sort | false | string | Default sorting order | |
supportMultiSort | false | boolean | false | If false, only support single field sorting |
asyncGetCount | false | boolean | false (defaults to true if ejb is not set or slave is not set) | If set to true, the count of lookup queries will be queried separately from the query data |
mustJoinSt | false | string | Used to define the stInfo that must be joined in this query. StInfo must be in the relation hierarchy of srcSt; Two or more stlnfos need to be separated by semicolons ; | |
ejb | false | string | The EJB to do search, if not provided the default EJB will be used. If you need to provide an EJB, it must extend GenSearchSqlAdapter | |
udfUse | false | boolean | true | If set to false, it will not appear in the UDF modules |
accessModule | false | string | The module corresponding to the current stSearch | |
accessField | false | string | “id” | Specify which field of srcSt is associated with the id field of the footer table |
nameCardSupport | false | boolean | true | Support nameCard or not |
hideFormat | false | boolean | false | If set to true, lookup will hide the format, and then user can not set the format |
hideFilter | false | boolean | false | If set to false, users can not add a filter condition on the lookup |
supportSearch | false | boolean | true | If set to false, users can not use mask search on the lookup |
supportDeletedData | false | boolean | false | If true, search can query the records that have been deleted |
slaveEjb | false | string | slave's class name | |
slaveApplyTo | false | string | The current slave can be used in other stSearch; fill in the stSearch name here; if you need to fill in more than two stSearch, please use the semicolon ; to separate them. | |
skipAccess | false | boolean | false | If set to true, lookup will skip access right |
slaveCol's attributes. The slaveCol tag is used to define a custom field that is returned as a result of the search. This tag is optional.
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
col | true | string | Field's name | |
mess | true | string | Field's mess code | |
pattern | false | string | Field's pattern | |
sql | false | string | If the value of this field is to be queried from the database, you need to input a string of SQL to this | |
cond | false | boolean | true | If the value is true, this field can be used as a query condition |
sort | false | boolean | true | If the value is true, this field can be used for sorting order |
show | false | boolean | true | If the value is true, this field can be displayed in the M18's UI |
followField | false | string | Fill a field under the current stInfo or associated table. Used to add the currently added slaveField behind the field in the left column of the format setting interface. |
bindCond's attributes. The compulsory condition for this stSearch
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
andOr | false | string | If not set, SQL will use AND to construct | |
leftBrackets | false | string | If needed, please fill "(" | |
leftFieldMode | false | string | "column" | Indicates the type of leftField |
leftField | false | string | If leftFieldMode is "column", then we will fill in the column name. If leftFieldMode is "value", then fill in the data | |
operator | fasle | string | "=" | Fill in the operator here; the default is "=" |
rightFieldMode | false | string | "value" | Indicates the type of rightField |
rightField | fasle | string | If rightFieldMode is "column", then we will fill in the column name. If rightFieldMode is "value", then fill in the data | |
rightBrackets | false | string | If needed, please fill ")" | |
condString | false | string | Fill in a SQL query here; If you set this, all the above parameters will lose their effect. |
format's attributes. format is used to configure the query condition
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
id | true | int | -1 | Can not be greater than 0 . If greater than 0 will be changed to - |
mess | true | string | format's mess code |
format's cond's attributes. The default query condition
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
andOr | false | string | If not set, will use "AND" to construct SQL | |
leftBrackets | false | string | If needed, please fill "(" | |
leftFieldMode | false | string | "column" | Indicates the type of leftField |
leftField | false | string | If leftFieldMode is "column"; then we will fill in the column name; if leftFieldMode is "value", then fill in the data | |
operator | fasle | string | "=" | Fill in the operator here; the default is "=" |
rightFieldMode | false | string | "value" | Indicates the type of rightField |
rightField | fasle | string | If rightFieldMode is "column"; then we will fill in the column name; if rightFieldMode is "value", then fill in the data | |
rightBrackets | false | string | If needed, please fill ")" | |
condString | false | string | Fill in a SQL query here; If you set this, all the above parameters will lose their effect. |
format's col's attributes. To configure default display fields.
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
col | true | string | Field's name | |
mess | false | string | Mess code | |
width | false | string | The width of the field in browse dialog table | |
sortable | false | boolean | true | Indicate the current column can be sorted. |
format's sort's attributes. To set the default sorting order.
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
col | true | string | Field's name | |
ascending | false | boolean | false | If set to true, it will be sorted using descending order |
namecard's attributes. Please read Name Card.
handler's attributes. The tag is used to configure the
init
method of the lookup parameterStParameter
. Theinit
method runs before the search data is loaded into the lookup field, and the page lookup field runs before the query.
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
name | true | string | The class name; the method inside the class must have @InitStSearchParam annotation |
stSearchInitHandler's attributes. The stSearchInitHandler tag is used to configure the class that initializes stSearch.
Name | Required | Type | Default Value | Description |
---|---|---|---|---|
stSearchName | true | string | stSearch's name | |
className | true | string | Fill a class that extends StSearchInitHandlerAdapter |
Note: By setting stInfo and stSearch, you can use "search" to query data
Configure XML: stInfo
<stInfo name="simpleUser" mess="share.user" table="user">
</stInfo>
<stInfo name="employee" mess="share.employee" table="employee">
<relation col="createUid" tarSt="simpleUser" tarCol="id"/>
<relation col="lastModifyUid" tarSt="simpleUser" tarCol="id"/>
</stInfo>
Configure XML: stSearch
<stSearch name="employee" mess="employee" srcSt="employee" maxLevel="3" sort="#employee#.code" accessModule="employee" asyncGetCount="true">
<bindCond leftField="id" operator="!=" rightField="0"/>
<format mess="core.defFormat">
<col col="code"/>
<col col="desc"/>
</format>
<namecard imgCode="photoCode" width="290"></namecard>
</stSearch>
# Search Process Customization
Note: In the search process, developers can configure slave or handler to modify the search process.
- Slave configuration: It needs to inherit the
SearchSlaveAdapter
. Then through the methodsbeforeDatalookup()
andafterDatalookup()
it overrides the search process. InbeforeDatalookup()
, developers can add conditions that can affect the query results. InafterDatalookup()
, developers can modify the query results before it return to users. - handler tag configured in stSearch.xml: It is the processing method to run before the lookup checking of data import, and before lookup result is returned in the page.
As you can see in the below example, the processing method in the handler needs to be annotated with @InitStSearchParam. The method must be static, and the parameters are also fixed (StParameter param, SqlEntity entity, String tableName, String colName, int row).
<handler name="com.multiable.core.share.modulestsearch.InitStParam"></handler>
public class InitStParam {
@InitStSearchParam(moduleName = "dept", stSearch = "dept")
public static void initDeptSupDeptParam(StParameter param, SqlEntity entity, String tableName, String colName, int row) {
if (!("dept".equals(tableName) && "supDept".equals(colName))) {
return;
}
}
}
- initHandler Class: Adding tag stSearchInitHandler in stSearch.xml can make 3PD further customize stSearch initialization.
handler class needs to inherit StSearchInitHandlerAdapter
.
<stSearchInitHandler stSearchName ="employee" className ="com.multiable.core.share.modulestsearch.EmpStSearchHandler"/>
public class EmpStSearchHandler extends StSearchInitHandlerAdapter {
@Override
public void initStSearch(StSearchInfo stSearchInfo) {
CawLog.info(stSearchInfo.getName());
}
}
- handler tag configured in stInfo.xml: It can let 3PD further customize stInfo's initialization.
handler class needs to inherit StInfoHandlerAdapter
.
<handler stInfoName ="employee" className ="com.multiable.core.share.modulestsearch.EmpStInfoHandler"/>
public class EmpStInfoHandler extends StInfoHandlerAdapter {
@Override
public void initStInfo(StInfo stInfo) {
CawLog.info(stInfo.getName());
}
}
# Self-defined Search
# Solution 1: Re-define EJB of Search
Note: If the system default constructed search SQL does not fulfill the needs, you can re-define the SearchEJB
Restriction: Need to extend GenSearchSqlAdapter
Example: Enter the name of ejb "GenEmptySQLEJB" in stSearch tag, then extends class GenSearchSqlAdapter
Example
@Stateless
@Local(GenSearchSql.class)
public class GenEmptySqlEJB extends GenSearchSqlAdapter {
@Override
public SearchSqlInfo getLookupSql(StSearchInfo myInfo, StParameter param) {
SearchSqlInfo emptyData = new SearchSqlInfo();
emptyData.setSearchSql("");
emptyData.setCountSql("");
return emptyData;
}
}
# Solution 2: Re-define EJB of Search and Slave
Note: If an stSearch data does not need to be queried from the database, you can use the redefined SearchEJB + use slave to generate data.
Example: Enter the name of ejb "GenEmptySQLEJB" and slaveEjb = "PatternInfoSlave" in stSearch.xml tag, ejb needs to extend GenSearchSqlAdapter
, slave needs to extend SearchSlaveAdapter
Java Code
GenEmptySqlEJB
: Please refer to the above GenEmptySqlEJB
example
PatternInfoSlave
: To generate data in slave, only needs to override afterDatalookup
Example
@Stateless
@Local(SearchSlave.class)
public class PatternInfoSlave extends SearchSlaveAdapter {
@SuppressWarnings("unchecked")
@Override
public void afterDatalookup(SearchResult result, StParameter param) {
SqlTable sqlTable = StLookupLib.createEmptyLookupTable();
sqlTable.addField(new SqlTableField("type", String.class));
List<String> keys = new ArrayList<>();
*** //do some search coding
int id = 1;
int startRow = param.getStartRow() < 1 ? 1 : param.getStartRow();
int endRow = param.getEndRow() > keys.size() ? keys.size() : param.getEndRow();
for (int i = startRow; i <= endRow; i++) {
CawPattern pattern = CawGlobal.getPattern(keys.get(i - 1));
int rec = sqlTable.addRow();
sqlTable.setLong(rec, "st_id", id);
sqlTable.setString(rec, "st_code", pattern.getCode());
sqlTable.setString(rec, "st_desc", pattern.getType());
sqlTable.setLong(rec, "id", id);
sqlTable.setString(rec, "code", pattern.getCode());
sqlTable.setString(rec, "type", pattern.getType());
id++;
}
result.setResultTable(sqlTable);
result.setTotalRows(keys.size());
}
}
# Solution 3: Implement New Lookup Component
Note: If existing lookup component can not meet the needs of users, then you can write your own components, and no longer use stSearch/ stInfo configuration.
Restrictions: Everything on the page and in the query needs to be handled.
# Login and Access Control
# Login Customization
loginHandler
During M18 system login, developers can set loginHandler tag in app.xml to intercept the process. Overriding the method checkLoginInfoBeforeLogin()
can handle before login event, while overriding afterLogin()
method can handle after login process.
loginHandler's attributes
Name | Description |
---|---|
className | class name of the handler, needs to extend LoginHandlerAdapter |
wsSkipAccess
For most web service requests to M18, access control will be checked with the userKey and authorization token. Using wsSkipAccess tag in app.xml can make a web service request skip such checking.
wsSkipAccess's attributes
Name | Description |
---|---|
pathNames | path of web service to skip the access checking, multiple paths can be separated by semicolons ; . Example: "/cawLogin/checkLoginUser;/image/getImage“ |
webSkipAccess
M18 will check session login status for non-web service requests as well, use webSkipAccess tag can make a web request to M18 to skip such checking.
webSkipAccess's attributes
Name | Description |
---|---|
pathNames | path of web request to skip session checking, multiple paths can be separated by semicolons ; . Example: "/CaptchaServlet;/wfAction” |
# Module Rights
M18 uses XML of module and navigation menu to configure available rights for a module. For details please refer to navmenu.xml.
# Data Guard
If a module has data guard enabled, when doing lookup a record in the module, data guard will be checked. For how to enable data guard in a module, please refer to module.xml.
The SQL related to data guard is put in the corresponding StSearchInfo.java
SQL of Data Guard
Name | Description |
---|---|
accessStr_noBe | SQL used when module is not BE specific |
accessStr_oneBe | SQL used when module is BE specific, and only one BE's records are queried |
accessStr_manyBe | SQL used when module is BE specific, and multiple BEs' records are queried |
accessStr_mir | SQL used when deleted records are queried |
Generation of data guard SQL
Upon stSearch initialization, system will run StSearchInfo
initAccess method to generate these query SQLs. It will construct the white list and black list SQL based on the data guard configuration. Then the data guard of all related lookup field's of the module will be considered. If user wants a lookup field to be skipped in data guard SQL, please mark skipAccess="true" in datadict.xml.
# How to Customize Data Guarded Results
Data Guard module has auto-calculate data guard function. During module save, it will auto-calculate related access condition data.
Note: Developer can implement entityHandler's autoCalcAccess method to intercept the data guard logic
entityHandler's attributes
Name | Type | Description |
---|---|---|
name | string | Module name to be affected. If empty, it will affect all modules. |
className | string | class qualified name. class needs to extend EntityHandlerAdapter |
# How to Replace Search's Data Guard
Developer can refer to Search Process Customization, point 3, to modify the data guard of search.
# Field Level Access Rights
All fields in system tables and the fields: code
, desc
, beId
in all tables cannot have field level access rights. Fields that are marked with buildin
= true
cannot have field level access rights.
Developers can disable field level access rights for all fields in a table by setting fieldRightSetting
= false
for a table in module.xml.
Note: Can use methods in RightLib
to get field rights.
public static List<FieldRightDto> getFieldRight(long beId, Set<String> tables);
// Get field rights for current user, input is beId and a set of table names
public static List<FieldRightDto> getFieldRight(long beId, Set<String> tables, long uid);
// Get field rights for a specific user (need to pass in uid)
# Common Functions
# About JBoss Configuration File
# cawserver.properties
cawserver.properties is a very important configuration file put under wildfly9/ config folder. It is used to configure startup parameters of M18 and JBoss.
cawserver.properties core parameters introduction
#jboss server's ID, if jboss cluster is setup, each node need to have diff. jboss id
caw.jboss.id=cl
#jboss server belongs to which cluster
caw.jboss.cluster=abc
#some task will only run in main server, in a cluster environment, there should be only one main server
caw.main.server=true
#path for dbdoc and appdoc
caw.fileData.dbDoc.path=/home/centos/xxxshare
caw.fileData.appDoc.path=/home/centos/xxxshare
#temp path for temp files
caw.file.app.path=home/centos/xxxshare/temp
#External URL for M18
caw.web.url=http://www.abc.com/jsf/
#Server internal URL
caw.internal.url=@web_url
#M18 DB IP
caw.database.ip=192.168.10.117
#M18 DB Port
caw.database.port=3306
#M18 DB Name
caw.database.dbname=ce01
#M18 DB User
caw.database.user=ce01
#M18 DB Pwd
caw.database.password=ce01abcdef
#Decide if current jboss can run scheduled task, 1 means can.
caw.sch.quartz = 1
# reset.caw
This file can be put under wildfly9/ config folder, which is useful for development environment. Developer can set any parameters of cawserver.properties here, when eclipse's JBoss is running, will use parameters in reset.caw to run.
#Set to 1 if want AP debug mode during jboss run
caw.ap.debug = 1
#during jboss startup, skip sync data structure or not
caw.ap.skip.sycnDB=1
#Below control if detail log message created for specifc module CRUD actions
caw.ap.debug.checker.create=0
caw.ap.debug.checker.save=0
caw.ap.debug.checker.read=0
caw.ap.debug.checker.delete=0
caw.ap.debug.checker.module=
#for lock and debug scheduled job, can enter multiple, separated by semicolons.
caw.ap.sch.debugJobName=
#for loc and debug scheduled job group, can enter multiple, separated by semicolons.
caw.ap.sch.debugJobGroup=
#lock job for how many hours
caw.ap.sch.debugTime=
#If set to 1, then report jasper report format update will be skipped
caw.report.skipUploadFormat = 0
# Table Structure Information
For M18's developers, understanding the data structure of existing modules/functions is essential for their development. In this part we will introduce how to find important table structure information and how to use API to lookup table structure information of a module.
In M18, developers can use [Data Dictionary] to find table/ field structure of any editor in the system:
In Java code, to get datadict info with known table name, developer can use this:
DdTable table = CawGlobal.getDdTable("employee");
Loop the DdTable object:
Ehcache cache = CawCache.getCache(CawCache.ddCache);
@SuppressWarnings("rawtypes")
List keys = cache.getKeys();
for (Object key : keys) {
DdTable table = (DdTable) cache.get(key).getObjectValue();
// add you code here
}
# Data Import and Export
M18 has data import/export functions for most modules, which is useful for data initialization, temporary backup and data migration to/ from other systems.
Data Export
If developers need to control the data in the exported excel, one can set excelGener
in module.xml for corresponding module, with excelGener
= "class qualified name". The class needs to implement IExcelGener
interface. Developers can use the method exportData
and appendMoreData
to manipulate exported data in excel.
Note: The default implementation class for IExcelGener is ExcelGener class.
public interface IExcelGener {
// Method to generate excel importation template excel
public Workbook createTemplate(String moduleName, long beId, String dataExportConfigJson) throws Exception;
// Convert SqlEntity data to excel
public Workbook exportData(DataExportConfig config, SqlEntity entity) throws Exception;
}
Data Import
If developers want to control excel importation, one can set excelGener
and dataImportConverter
in module.xml for corresponding module, with excelGener
= "class qualified name". The class needs to implement IExcelGener
interface. dataImportConverter
needs to extend ImportConverterAdapter
class.
One can use IExcelGener
createTemplate
method to create an excel template for importing data. One can use the dataImportConverter
convert method to convert excel data to the M18 system's SqlEntity structure; One can use the getImportResultWriter
method to get the ImportResultWriter
for writing error messages to an excel file.
Note: The M18 system uses the EntityImportConverter
class to convert the imported excel data by default. The error information is written to the excel file by using the ExcelImportResultWriter
class;
Note: If you want to intercept the import process without the need to redefine IExcelGener
and dataImportConveter
, there is one more way: define tag entityHandler in app.xml:
entityHandler's attributes
Name | Type | Description |
---|---|---|
name | string | Module name, if empty will affect all modules |
className | string | class qualified name, class need to extend EntityHandlerAdapter |
Below methods in entityHandler are related to data import:
public Map<String, Set<String>> getImportSkipColumn(String moduleName, long beId);
public Map<String, List<DdColumn>> getImportExtraColumn(String moduleName, long beId);
public List<String> handleExtraBeforeAllColumn(String table);
public void updateExtraColumnValue(SqlEntity entity, int sqlTableRow, String moduleName, String tableName, DdColumn ddColumn, Object cellValue, long beId);
public Set<String> getImportSkipTable(String moduleName, long beId);
public default String modifyColumnsRemark(String remark, String tableName, DdColumn col, DataExportConfig config) {
return remark;
}
entityHandler's methods related to data import:
Name | Description |
---|---|
getImportSkipColumn | Return Map <String, Set <String >>; Map's key is the table name, set is the fields that will skip import |
getImportExtraColumn | Return Map <String, Set <String >>; Map's key is the table name, list is the DdColumn List; Used to indicate which table in the current module needs more fields to import. These fields will be displayed in the import module and import excel template |
handleExtraBeforeAllColumn | Return a list, indicates which extra fields need to be handled before all fields |
updateExtraColumnValue | Used to process extra field values |
getImportSkipTable | Returns a set indicating which tables in the current module will not be able to use the import function |
modifyColumnsRemark | Used to modify the column remarks in excel importation template |
# Document Printing
M18 system supports document printing using ireport. It converts data and report format to PDF using JasperReports API. User can then print the PDF directly.
Before handle report format, please make sure you have basic understanding of ireport.
Please install the latest version of ireport on your computer and replace com.jaspersoft.studio.data_6.2.0.final.jar in M18 with the installation directory. Note: ireport editing software is an Eclipse.
Please add caw share jar to JaspersoftStudio project, so that the system can give the correct information/messages when dealing with M18 functions.
#
# docReport.xml
report's attributes
Name | Type | Description |
---|---|---|
code | string | Used to uniquely identify a report. Recommended the code starts with @ |
module | string | Module code related to this printing. Support multiple input, each separated by semicolons ; |
providerCode | string | Print provider class name |
reportDto | string | Dto's class name, which provides the report parameters |
reportDtoSettingSrc | string | Dto's setting source |
providerMess | string | Print provider messCode |
providerOrder | int | Valid when there is more than one print provider in a module. Decide the print order of the provider |
apDebug | boolean | When true, only visible in debug mode |
format's attributes
Name | Description |
---|---|
code | format code |
description | format's description |
remark | format's remarks |
packageName | jrxml location |
jrxml's attributes
Name | Description |
---|---|
name | jrxml's name, should end with .jrxml |
main | Mark the main jrxml in a format |
resetData's attributes
Name | Description |
---|---|
prCode | provider's class name |
resetClass | class name, implements CawJrPrintRestData |
reportHandler's attributes
Name | Description |
---|---|
reportCode | report code |
handlerClass | class name, needs to implement ReportHandler . Allow developer to modify the jrxml during initialization, normally used to hide some functions in the report |
reportShowHandler's attributes
Name | Description |
---|---|
prCode | provider's class name |
handlerClass | class name, needs to implement ReportShowHandler . Can control if a pdf should be downloaded directly or do other operations |
Example:
<docReport>
<report module="employee" code="@bpm_emp"
reportDto="com.multiable.core.share.data.ireport.emp.EmpJrDto" reportDtoSettingSrc="/view/dialog/empPrintSetting.xhtml"
providerCode="com.multiable.ireport.provider.emp.EmployeeProvider" providerOrder="1" providerMess="jr.emp_provider" apDebug ="true">
<format code="@bpm_emp" description="Print" remark="Print Employee" packageName="com.multiable.core.share.data.ireport.resource.emp">
<jrxml name="emp.jrxml" main="true"></jrxml>
<jrxml name="empPic.jrxml"></jrxml>
<jrxml name="empUser.jrxml"></jrxml>
</format>
</report>
</docReport>
# Add Provider Class and SQL
Dto class
public class EmpJrDto extends ModuleReportDto {
private boolean prtPic = true;
@Override
public String getDefaultTitle() {
return "Testing Ireport!";
}
public boolean isPrtPic() {
return prtPic;
}
public void setPrtPic(boolean prtPic) {
this.prtPic = prtPic;
}
}
This is a simple POJO class, this dto must extend ModuleReportDto
Provider
public class EmployeeProvider extends ModuleProvider {
@Override
public void initReportStru(CawReportDataSet reData) {
// set table alias name
reData.setAlias(0, "EMPLOYEE", "employee");
reData.setAlias(1, "DEPT", "dept");
reData.setAlias(2, "EMPLOYEEPIC", "employeepic");
reData.setAlias(3, "PICUSER", "user");
reData.setQuery("EMPLOYEE");
}
@Override
public void adjustData(CawReportDataSet reData) {
reData.setRelationTo("EMPLOYEE", "dept", "id", "DEPT");
reData.setRelationTo("EMPLOYEEPIC", "userId", "id", "PICUSER");
reData.assignSubReport("EMPLOYEE", "EMPLOYEEPIC", null, "id", "hId");
}
@Override
public CawReportDataSet genIdsData() {
String sql = "{Call prtEmployee('" + getReportDto().getMainIdString() + "')}";
MacQuery query = new MacQuery();
query.setQuery(sql);
CawReportDataSet ds = fillAndAdjustData(query);
return ds;
}
}
SQL
drop procedure if exists prtEmployee;
DELIMITER $$
CREATE PROCEDURE prtEmployee(in idStr varchar(2000))
BEGIN
create temporary table if not exists prtemptempTable
select a.*
from employee a
where find_in_set(a.id, idStr);
#create index
create index I_Employee_dept ON prtemptempTable(dept);
#main query 0
SELECT * from prtemptempTable;
#dept 1
select * from dept a
where exists(select NULL from prtemptempTable b where b.dept=a.id);
drop table prtemptempTable;
END$$
DELIMITER ;
Note: SQL should mostly use "select *" to get all fields in a table, such that when UDF fields are added in the future the SQL does not need to modify again.
Start JBoss (Use Jaspersoft Studio data source), then write JrXml
Note: URL above is http://127.0.0.1:8080/jsf/rfws/cawProvider/fields/com.multiable.ireport.provider.emp.EmployeeProvider?accessToken=iz&lang=zh_CN
accessToken please use the caw.ws.glAccessToken in cawserver.properties
The field returned by lang specifies the language of mess (when not transmitting, it uses the lang
of GlobalOption
)
If skipLang is true, the returned field will not add mess description (optional, the default is false)
Note: Not limited to use the web service "cawProvider / fields", if necessary, developers can design their own process by referring to CawJrProviderEJB
# UDF Editor's Template
Developers can implement the UDF template that will be used in UDF editor created by end users, like the trading or finance editors template created by trading and finance App in M18 ERP.
module's attributes
Name | Required | Type | Description |
---|---|---|---|
code | true | string | Template's Code |
description | true | string | Template's mess code |
module | true | string | Corresponding module of the template, the module needs to have template="true" in module.xml |
helper | true | string | CawUeHelper Helper class name, needs to extends CawUeHelper |
<page> | true | string | The initial page of the UDF editor generated by this template |
<setting> | true | string | The setting specific to this UDF template |
Example: helper of CawUeHelper
public class FmTemplateHelper extends CawUeHelper {
private static final long serialVersionUID = 5554948686647491057L;
@Override
public void initModuleInfo(SqlTable ue, int row, Module module) {
String jsonStr = ue.getString(row, "backEndInfo");
if (!StringLib.isEmpty(jsonStr)) {
FmtOption option = JSON.parseObject(jsonStr, FmtOption.class);
module.setUseAttach(option.isSupportAttachment());
} else {
module.setUseAttach(false);
}
module.setFmShare("A");
}
}
# Initial Data
M18 system database information is automatically generated by JBoss through java codes. Therefore, developers can not keep any preset data in the database.
To create initial/default data to database, developers can set entityDataInit in app.xml:
Name | Description |
---|---|
name | entityDataInit's name |
initDataCreator | class name, extendsInitDataCreator , create data in the init() method |
Note: This method runs every time Wildfly is started. Please check if the data that needs to be initialized already existed in database.
Example
public class TestDriverCreator extends BaseDataCreator {
@Override
public void init() {
if (hasEntityData()) {
return;
}
// Init test
SqlEntity entity = create();
JdbcDriverEntity accseeDriver = EntityLib.toObject(entity, JdbcDriverEntity.class);
accseeDriver.setCode("test");
accseeDriver.setDesc("test");
accseeDriver.setJdbcType(JdbcType.Driver);
accseeDriver.setClassName("net.test.jdbc.TestDriver");
accseeDriver.setGrammarType(GrammarType.UDF);
accseeDriver.setIndepPw(true);
accseeDriver.setWithDbName(false);
accseeDriver.setConnStrTplt("jdbc:test://${[@D_FilePath]};memory=false");
EntityLib.mergeToEntity(accseeDriver, entity);
update(entity);
}
}
# Data Fix
During M18 program update, data fix may need to be carried out to handle data fix. Developers can define dataFix in app.xml.
Name | Description |
---|---|
className | class name, needs to extend DataFixAdapter , handles the data fix in run() method. Note: This is also an unique key |
ap | Mark the programmer name who writes this data fix |
desc | Description of the data fix |
order | Indicate the order of the data fix. |
Example
<dataFix className="com.multiable.core.ejb.bean.cache.datafix.TestToFix" ap="Test" desc="fix test"></dataFix>
public class TestToFix extends DataFixAdapter {
@Override
public int run() {
String fixSql = "update testinfo set myIdType = 'text'; ";
try {
CawDs.runUpdate(fixSql);
} catch (SQLException e) {
CawLog.logException(e);
}
return 0;
}
}
Note: Please write the data fix carefully, and ensure all error cases are handled.
Note: The data fix will only run one and only one time, success or not.
# How to Add JAR
To make sure that the corresponding third-party jar package is available while Wildfly is running, you need to ensure that the jar has been added to the Wildfly runtime.
M18 recommends using module.xml (Wildfly) way to deal with it. M18 also recommends modifying both MANIFEST.MF
and jboss-deployment-structure.xml
.
Note: No compilation errors during development does not mean your code can run in JBoss runtime successfully.
Note: Wildfly has its own way of classloader management.
Note: At the time of project launch, Wildfly will automatically copy the lib under the ear (or the lib below the war). If the project is deployed multiple times, it will take a lot of hard disk space.
For detail please refer to Wildfly Docs (opens new window).
# Identify the Source of JBoss Startup Errors
Due to configuration/ jar update/ code/ system environment and so on, M18 startup sometimes fails. To debug, please always check and fix the first error you found during startup, and then try to restart JBoss and see if other error happens, and fix it and restart again, and so on.
- Do not look for mistakes from bottom to top of error messages
- Do not try to solve all the errors in the log prompts, always fix the first error first and restart
- Do not assume that the problem must be related to java codes
- If you find that there is not enough information, it is recommended to modify your codes and print more log out
- If in Eclipse, it is recommended to delete all breakpoints and try restart
# Schedule Tasks
# Create Schedule Task
Create a Job Class
Job class need to extend CawQuartzJob
, and implement the doExecute method:
public class SyncJob extends CawQuartzJob {
public static String JOBGROUP = "caw.sync";
@Override
protected CheckMsg doExecute(JobExecutionContext context, CawJobDto dto) throws Exception {
//do something
return msg;
}
}
Publish a Job
First build a CawJobDto
object, use the method CawJobLib.scheduleJob (dto) to release. CawJobDto
has a three-parameter-constructor, the first is jobGroup, the second is the current entity id, the third is job class. jobGroup + id must be a unique jobName. There is need to provide TimeScheduleDto
. It is the dto component of the time, can be generated by the configuration page or you can create an instance of it using new
. There is a jsonData in CawJobDto
, you can fill some job parameters, which can be obtained in dto in doExecute.
CawJobDto dto = new CawJobDto(SyncJob.JOBGROUP, id, SyncJob.class);
dto.getJsonData().put("id", id);
dto.setTimeSchedule(timeDto);
dto.setJobMess("syncJob");
CawJobLib.scheduleJob(dto);
Delete a Job
Use the deleteJob method in CawJobLib
class.
CawJobDto dto = new CawJobDto(UnlockUserJob.JOBGROUP, param.getEntityId(), UnlockUserJob.class);
CawJobLib.deleteJob(dto);
# Job in a JBoss Cluster
After a job is published, the information will be saved to the cawjob table; if the job expires, there will be scheduled tasks to remove these expired jobs.
Only when the server cawserver.properties is configured caw.sch.quartz = 1, the server can run M18 jobs.
Note: This server will not run jobs when the server's caw.sch.quartz = 0. However, the jobs posted from this server can be assigned to a target server via the caw.sch.targetUrl and caw.sch.targetAccessToken configurations. To post job to a target server, the target server must be able to run the job.
cawserver.properties
//this server can run schedule job
caw.sch.quartz = 1
//the target server to run a job
caw.sch.targetUrl = http://127.0.0.1:8080/jsf/rfws/
//access token for the target server
caw.sch.targetAccessToken=iz
# How to Test Scheduled Tasks
If you want to debug a job during development, you need to ensure the job will run at your local JBoss, which means other JBoss servers will not run the job ahead of your own server, the following parameters need to be set in reset.caw:
reset.caw
#lock the job to run locally, support multiple input, separated by semicolons
caw.ap.sch.debugJobName=caw.sync.1
#lock the job group to run locally, support multiple input, separated by semicolons
caw.ap.sch.debugJobGroup=caw.sync
#how many hours for the job to lock in this server
caw.ap.sch.debugTime=5
SQL Tables related to scheduled tasks
Table Name | Description |
---|---|
cawjob | Store published schedule task details |
cawjoblog | Scheduled task log |
cawjoblock | Scheduled task lock info |
# File Stream Handling
All files in M18 are stored in fileData
table:
- One file corresponds to one record in
fileData
, system uses the MD5 + size to identify the uniqueness of the file - Files saved on
filedata
do not provide the delete function - In order to avoid user reading the picture traversal (picture does not need to verify the login link), the system uses
imgcode
File Operation
FileCurdEao fileEao = JNDILocator.getInstance().lookupEJB(FileCurdEao.class);
byte[] byteArray = **
//Save file in memory to db
long fileId = fileEao.saveFile("pdf", byteArray, "work.pdf");
String filePath =**
//Save File object to db
fileId = fileEao.saveFile("pdf", filePath, "work.pdf");
//Read file from M18
String filePath_read = fileEao.getFilePath(fileId);
Image Operation
ImageLocal imageEJB = JNDILocator.getInstance().lookupEJB("ImageEJB", ImageLocal.class);
InputStream inputStream = ***
// Upload image, and get imgCode
String imgcode = imageEJB.uploadImage("imgName", inputStream);
// Get image from imgCode
File img = imageEJB.getImage(imgcode, false);
// if 2nd para is true, thumbnail will be returned
# M18 JBoss Cache
Data that need to be frequently queried but not always modified, can be stored in CawCache
, which is a application server cache in JBoss. Such data include but are not limited to datadict, mess code and module configuration, etc.
# CawCache Usage
Developers who want to add a cache, can use the CawCache
method to add. See CawCache
addSelfPopulatingCache
method
Then use the CawCache
putObject
or remove method to manipulate the data in the cache.
Through the getObject
method of CawCache
, you can get the data of the cache.
//The first parameter is the name of the cache, the second parameter refers to the max record count of the cache, the third parameter refers to the cache expiry time (in seconds); if the 4th parameter is true, When reading this cache to copy an object out to return
CawCache.addSelfPopulatingCache(navMenu, CawLib.getBaseCacheSize() * 2, 86400, false, new NavmenuEntryFactory());
//The first parameter is the name of the registered cache; the second parameter is the key to be cached in this cache, which needs to be a serializable key; the third parameter is the content of the cache to be placed in this cache
CawCache.putObject(cawcheName, key, object);
//The first parameter is registered cache name; The second parameter is the cache inside the cache key
MenuItemConfig menuCfg = (MenuItemConfig) CawCache.getObject(CawCache.menuCache, menukey);
CawCache.remove(CawCache.userSettingCache, uid);
# Cache in JBoss Cluster
In the case of clustering, due to data changes, the cache may appear out of synchronization in different JBoss servers;
Note 1: When using a cache, you have to think about how to synchronize your data or you will have unpredictable problems (not just for CawCache
)
Note 2: Not all caches need to be synchronized
Two ways to synchronize cache within a JBoss Cluster.
The default synchronization cache method
Recommended usage: The contents of the cache is simple, just do a simple put or delete CawCache
.
Please use the method callCawCacheClusterEvent
of CawClusterLib
Add cluster cache
CawClusterCacheDto dto = new CawClusterCacheDto();
dto.setName(UseInfoCache.CACHE_KEY);
dto.getCacheMap().put(menuCode, uic);
dto.setCacheClass(UseInfoCache.class);
CawClusterLib.callCawCacheClusterEvent(dto);
Remove cluster cache
CawClusterCacheDto dto = new CawClusterCacheDto();
dto.setName(UdfLogic.CACHE_KEY);
dto.getDeleteKeys().add(code);
CawClusterLib.callCawCacheClusterEvent(dto);
CawClusterCacheDto
Name | Type | Description |
---|---|---|
name | String | Cache name that need to sync |
cacheMap | Map<Serializable, Object> | The cached values in this map will be added to other machines in the cluster, and the cached map is key-value structure |
cacheClass | Class<?> | Class of the value in cacheMap |
deleteKeys | Set<Serializable> | The set of keys that to be deleted in other servers in the cluster |
Self defined cache synchronization
Need to synchronize CawCache
or some static Map, Collection and other caches, some caches may not simply add or delete, or need to operate several different caches, then you need to customize the class to deal with;
Configure clusterListerner in app.xml:
clusterListerner's attributes
Name | Type | Description |
---|---|---|
eventType | string | Cache event type |
className | string | class name, class need to extend CawClusterListenerAdapter |
The callClusterEvent method of CawClusterLib
is called when the cluster cache needs to be synchronized. Then the machines of other clusters will run the corresponding listener's method:
JSONObject jo = new JSONObject();
jo.put("cacheName", JsfCache.theme);
jo.put("cacheKey", (long) action.getData(ActionParam.id));
//The first parameter is xml eventType configuration; the second parameter is a String, usually pass a json, the cluster server to get this parameter will be passed to the clusterListerner run method;
CawClusterLib.callClusterEvent(CawClusterEvent.JSFCACHE, jo.toJSONString());
# Object Sharing
M18 system supports data sharing between different M18 sites via create/install objects. To use this function one needs to set dataObject in app.xml.
dataObject's attributes
Name | Required | Type | Default | Description |
---|---|---|---|---|
className | true | string | Full class name. class needs to extend ModuleObjectHandler |
dataObject example
<dataObject className="com.multiable.core.ejb.bean.dataobject.handler.ComboObjectHandler" ></dataObject>
DataObjectHandler example
public class ComboObjectHandler extends ModuleObjectHandler {
@Override
public String getName() {
return "comboData";
}
@Override
public String getMenuCode() {
return "comboData";
}
@Override
public String getModuleName() {
return "comboData";
}
@Override
public String getMess() {
return "comboData";
}
@Override
public List<String> getInfoMessList() {
List<String> messList = new ArrayList<>();
messList.add("dataObject.comboDataInfo1");
return messList;
}
@Override
public Set<String> getDepends() {
Set<String> depends = super.getDepends();
depends.add("udfMess");
return depends;
}
}
ModuleObjectHandler implements DataObjectHandler, some methods stated here:
public interface DataObjectHandler {
// Import object upload file will run this method, you can override this method to intervene in the generation of entity
public DataObjectBaseDto restoreDsDto(DsEntryObject entryObj);
// Export object, you can override this method, some of the picture file type information written to the derived Object inside;
public DsEntryObject genDsEntry(DataObjectBaseDto dto);
// Export object, you can override this method to interfere with the export entity
public void fillupDto(DataObjectBaseDto dto, DataShareObjectDto dsDto);
// Import object to install, you can override this method to interfere with the installation of object
public Map<Long, CheckMsg> install(long beId, DataObjectBaseDto dsDto, List<DsInstallDto> insList, Map<String, Map<Long, Long>> depMapping, Object detail);
}
# Common Utility Classes
# Basic Operation
Class | Description |
---|---|
ArrayLib | Tools to deal with the data; Worth noting 1. Check whether the array contains a string, 2. Merge the array |
ClassLib | Tools to deal with classes: 1. Get the class name, 2. Get the class Field 3. Get the class method 4. Get the type of class |
DateLib | Tools to deal with dates |
DateFormatLib | Tools to deal with date formats |
ListLib | Tools to deal with list, and provide method to compare two lists |
MathLib | Tools to deal with common math functions like rounding, max, min, etc. |
StringLib | Tools to deal with string, and provide method to convert between string and html |
# Special Handling
Class | Description |
---|---|
SqlTableLib | Tools to deal with SqlTable, and provide methods to do POJO and SqlTable conversion, and JSON and SqlTable conversion. |
CheckMsgLib | Tools to deal with CheckMsg, which is a common response object in entity CRUD actions/web services. |
ConvertLib | Tools to deal with conversion of different data types |
CawLib | Tools to deal with CAW configuration, like getting parameters in cawserver.properties |
# Send E-mail Example
@EJB(beanName = "MacEmailSenderEJB")
private MacEmailSenderLocal emailEJB;
***
MacEmail email = new MacEmail();
for (MacEmailAttachment atta : getAttachments()) {
email.addAttachment(atta);
}
email.setTo("test@mac.com");
email.setCc("test1@mac.com");
email.setSubject("sunject - test");
email.setContent("conent");
CheckResult result = emailEJB.sendEmailWithId(email, emailSettingId);
***
Note: emailSettingId is the SMTP record ID in M18 system.
# Audit Trail's Example
List<AuditTrailData> datas = new ArrayList<>();
AuditTrailData data = new AuditTrailData();
data.setCode(code);
data.setDesc(desc);
data.setEntityId(entityId);
data.setExeTime(System.currentTimeMillis() - start);
data.setiRev(iRev);
data.setMenuCode("user");
data.setModuleName("user");
data.setOperation("test");
data.setOperationDesc("failed");
datas.add(data);
AuditTrailLib.addAuditTrail(datas);