ERP 2.50:Developers Guide/How to create build validations and module scripts
Languages: |
Contents |
Objective
In this howto article, we will create both a Build Validation and a Module Script. They are both very similar concepts, and are also implemented in a very similar way, so we are going to describe them in the same document.
Build Validations
A build validation in Openbravo is a class which is executed at the beginning of the update.database task, and which returns a list of errors. If there is one or more errors, the build is stopped, and the errors are shown to the user.
Because of this, the errors are shown to the user, the validation must follow these rules:
- In a standard way prompt message shows up explaining that the validation has failed but the system remains stable. This is very important
- In the below message has to be clear the cause of the failure and if need it an alert rule must be created to help users to identify the problematic records if finally the build fails.
- Every developer must ensure and check that the message is understandable once he creates the validation
Please see this image as an example:
- Starting from 2.50MP21 you can find a clear and simple example of how to develop a validation containing alerts in:
- src-util/buildvalidation/src/org/openbravo/buildvalidation/Cbpvendoracct_data.xsql
- src-util/buildvalidation/src/org/openbravo/buildvalidation/Cbpvendoracct.java
- From 3.0MP11 you can check:
- src-util/buildvalidation/src/org/openbravo/buildvalidation/AccountingTabs_data.xsql
- src-util/buildvalidation/src/org/openbravo/buildvalidation/AccountingTabs.java
The build validations main use is to stop the build because the instance is in such a state that rebuilding the system cannot be safely done. In modules, validations can be used to stop the build in case the module cannot be installed safely in the system for some reason.
So, for example, a validation could be used to check if the user has done a necessary manual setup step that cannot be automated. Or a validation could also be used to check if the user has filled some data which is needed for the module to be installed correctly.
Build validations are a different piece of code in Openbravo, in that they are distributed as binaries (compiled classes), which are executed directly without them being compiled on the run. The packaging task for modules will automatically include the binaries inside the .obx file, the developer should only make sure that the classes were compiled before packaging the module.
Introduction to the implementation of build validations
The main steps to create a build validation are:
1.- Write it (you need to create a Java class for each validation you want to do).
2.- Compile it (Build validations are compiled separately from the rest of the Openbravo code, using a specific ant compile task).
3.- Test it (you should carefully test the validation before including it in your module, or in Core).
4.- Add it to the module, or Core (Don't forget to add the binary classes too, as they are needed, in addition to the Java source files. ant package.module will automatically add the files to the .obx file, but you need to manually add the binary files in addition to the source files to the Mercurial repository if you are using one for your module, or if you want to add the validation to Core).
To create a build validation, you need to create a class which extends the
org.openbravo.buildvalidation.BuildValidation class. This is an abstract class which has one abstract method: List<String> execute()
This method needs to be implemented. The class will be loaded at the beginning of the update.database task, and this method will be called. As you can see, a List of Strings must be returned. If the list is empty, the build will continue. If the list contains at least one string, every string will be shown to the user, and the build will stop. Every string is supposed to be a meaningful error message, which shows the user what does he need to fix in his system for the validation to pass.
Writing the build validation
As was just explained, a build validation is nothing more than a class which extends the org.openbravo.buildvalidation.BuildValidation class, and implements the abstract execute method. Here you can see a small example:
public class ValidationExample extends BuildValidation { public List<String> execute() { try { ConnectionProvider cp = getConnectionProvider(); PreparedStatement ps = cp .getPreparedStatement("SELECT COUNT(*) FROM C_Bpartner WHERE name IS NULL"); ps.execute(); ResultSet rs = ps.getResultSet(); rs.next(); ArrayList<String> errors = new ArrayList<String>(); if (rs.getInt(1) > 0) { errors .add("There are business partners which don't have a defined name. Please fill the name of every business partner before installing the module MyModule."); } return errors; } catch (Exception e) { return handleError(e); } } }
In this example, you can see the main points of any build validation:
- The class implements the BuildValidation class, and its corresponding execute method.
- In the method, you can do the validation (i.e. check if the Openbravo instance complies with some specific rule). There is a convenient getConnectionProvider() method, provided by the abstract superclass, which allows you to do direct queries to the database. It is very important to remark that validations should only do queries to the database, they should never change the contents of the database. Module Scripts can be used to change the database if needed.
- Finally, the method returns a List of error Strings, which can be empty if the validation went well, or can contain one or more errors if the system didn't comply with the validation.
You can also use SqlC if you want to do the database operations.
public class ValidationExample2 extends BuildValidation { public List<String> execute() { try { ConnectionProvider cp = getConnectionProvider(); ArrayList<String> errors = new ArrayList<String>(); int numBpartners=Integer.parseInt(ValidationExample2Data.queryBPartners(cp)); if (numBpartners > 0) { errors.add("There are business partners which don't have a defined name. Please fill the name of every business partner before installing the module MyModule."); } return errors; } catch (Exception e) { return handleError(e); } } }
This needs the following xsql file:
<?xml version="1.0" encoding="UTF-8" ?> <SqlClass name="ValidationExample2Data" package="org.openbravo.buildvalidation"> <SqlMethod name="queryBPartners" type="preparedStatement" return="string"> <SqlMethodComment></SqlMethodComment> <Sql><![CDATA[ SELECT COUNT(*) FROM C_Bpartner WHERE name IS NULL ]]> </Sql> </SqlMethod> </SqlClass>
The class source files themselves should be inside the module folder, in the folder: src-util/buildvalidation/src
And they should follow the standard Java package rules. The folder will not exist if it's your first validation, so you will need to create it.
Compiling the build validation
To compile the build validation, you use the following command:
ant compile.buildvalidation -Dmodule=javapackageofmodule
If you want to compile the validations of Openbravo Core, the module property needs to be set to "org.openbravo"
This task will compile the Java classes, and copy them to the correct "build" folder in the module, or in the src-util/buildvalidation of Core.
Executing the build validation
The build validations will be automatically executed in the update.database task, or in update.database.mod if the module is being applied. It is very important to remark that the validations will not be compiled in this task, they need to have been compiled previously to be executed.
Another important point to remark is that the build validations will be executed in every version of the module, or Core, in which they are present (this means, for example, that if a validation exists in Openbravo Core MP15 and later, it will be executed when updating to MP15 or later, and will be executed again when updating to a later version). The developer needs to take this into account (it is advisable not to design validations which are version-dependant, they should always be designed to be generic).
The package.module task will automatically include the compiled binaries in the .obx file, provided that you compiled the validations before packaging the module.
If you are adding a validation to Openbravo Core, remember that you need to include the binary classes (contained in src-util/buildvalidation/build/classes/) to the repository like you would add any source files. If you don't, they won't be executed (because the classes are not compiled by default in the build process, they are executed only if the binary files are there).
Module Scripts
A module script is a task that is executed when a module is being applied in the database. This task is supposed to do operations that need to be done in the database, and cannot be done in a different way.
Conceptually, they are extremely similar to Build Validations, and the main steps involved to create a Module Script are virtually the same, so it's advisable that you read the previous section before this one. The main points of module scripts are the following ones:
- Whereas build validations are executed at the beginning of update.database, module scripts are executed in the middle of the process, when the foreign keys and triggers of the database have been disabled.
- As with build validations, module scripts will be executed every time an update.database or update.database.mod for that particular module is called. This means that the script must be built in such a way that can be repeatedly executed without problems, and this is a very important consideration that the developer needs to take into account.
- The module script should never fail. If it fails, the build will be stopped, but as it has already been started, the system will be in an inconsistent state (for example, all the foreign keys and triggers will be down). The developer must avoid this at all costs if possible.
Introduction to the implementation of module scripts
As with build validations, a module script is a Java class which extends the abstract class org.openbravo.modulescript.ModuleScript, and implements the execute() method. This execute method will be called by the update.database task. This method will include the logic of the module script, here all the operations will be done.
Writing the module script
Here goes a very simple Module script, which just sets the value of some column to a default in case the column value is null:
public class ModuleScriptExample extends ModuleScript { public void execute() { try { ConnectionProvider cp = getConnectionProvider(); PreparedStatement ps = cp .getPreparedStatement("UPDATE mytable SET mycolumn=0 WHERE mycolumn IS NULL"); ps.executeUpdate(); } catch (Exception e) { handleError(e); } } }
The main two important points that you need to take into account when building the script are:
- The script should never fail. Failure will leave the user in a very unfriendly situation (with the build stopped in the middle), and should be avoided at all costs. In this case, this script will not fail unless the table doesn't exist.
- The script needs to be designed so that it can be executed an indefinite number of times. This example script, for example, can be executed several times without problems (the first time will set all rows which have a null value, and after that, only the new rows which have been inserted before will be modified, but the previously modified rows will not be modified again unless they have the supposedly wrong null value again).
The class source files themselves should be inside the module folder, in the folder:
src-util/modulescript/src
And they should follow the standard Java package rules. The folder will not exist if it's your first script, so you will need to create it.
Compiling the module script
To compile the module script, you use the following command:
ant compile.modulescript -Dmodule=javapackageofmodule
If you want to compile the scripts of Openbravo Core, the module property needs to be set to "org.openbravo"
This task will compile the Java classes, and copy them to the correct "build" folder in the module, or in the src-util/modulescript of Core.
Executing the module script
The module scripts will be automatically executed in the update.database task, or in update.database.mod if the module is being applied. It is very important to remark that the module scripts will not be compiled in this task, they need to have been compiled previously to be executed.
Another important point to remark is that the module scripts work as build validations in regards to execution criteria; that is, they will be executed in every version of the module, or Core, in which they are present (this means, for example, that if a script exists in Openbravo Core MP15 and later, it will be executed when updating to MP15 or later, and will be executed again when updating to a later version). The developer needs to take this into account (it is advisable not to design scripts which are version-dependant, they should always be designed to be generic).
And, as it was explained above, scripts should also be designed to produce the same result if executed multiple times, because they will be executed every time the system is rebuilt, or the module is updated.
The package.module task will automatically include the compiled binaries in the .obx file, provided that you compiled the module scripts before packaging the module.
If you are adding a module script to Openbravo Core, remember that you need to include the binary classes (contained in src-util/modulescript/build/classes/) to the repository like you would add any source files. If you don't, they won't be executed (because the classes are not compiled by default in the build process, they are executed only if the binary files are there).
Languages: |
ERP 2.50:Developers Guide/How to create a new REST webservice | ERP 2.50:Developers Guide/How to define an oncreatedefault