Openbravo Architecture

Integrating the Data Access Layer with Openbravo XSQL and ConnectionProvider

donderdag 28 januari 2010

More and more developers are using the Data Access Layer (DAL) for coding business logic in the Openbravo application. I sometimes get questions related to how to integrate DAL constructs with standard Openbravo approaches like Stored Procedures and XSQL generated classes.

As there are several options I felt it was time to spend a blog on this. In this blog I will discuss a number of 'good-to-know' classes which can be handy when you want to integrate DAL with the classic Openbravo approaches.

The first one to cover is the probably the most important one: the DalConnectionProvider. The DalConnectionProvider can be used when you call utility methods which need a ConnectionProvider object. The great thing of the DalConnectionProvider is that it shares the same connection and transaction as the DAL itself. Let's take an example. This one is from the MassInvoicing module, computing the DocumentNo for a new invoice:

// create a new invoice object
final Invoice invoice = OBProvider.getInstance().get(Invoice.class);
// set some properties for the invoice
invoice.setOrganization(org);
invoice.setBusinessPartner(bp);
invoice.setInvoiceDate(invoiceDate);
invoice.setAccountingDate(invoiceDate);
// .... code truncated for clarity

// now compute a documentno, in the same database transaction
final String documentNo = Utility.getDocumentNo(conn,
new DalConnectionProvider(), vars, "", Invoice.TABLE_NAME,
invoice.getDocumentType().getId(),
invoice.getDocumentType().getId(), false, true);
invoice.setDocumentNo(documentNo);


The above code creates an invoice and then calls a stored procedure using the DalConnectionProvider as the connect provider. As the same transaction is used any updates done by the stored procedure can be committed together with the rest of your work. As you can see the DalConnectionProvider is easy to work with as it does not need any extra parameters, you can create and use it directly.
Sometimes it makes sense to call OBDal.getInstance().flush() before calling a stored procedure. This ensures that Hibernate has flushed all your changes to the database so that the stored procedure can see them. Also read the remark at the end of this blog!

The next two are somewhat related: CallProcess and CallStoredProcedure, they both call/run database logic in the database. The first, the CallProcess, can be used to call a process (defined in AD_PROCESS) directly. You can get a CallProcess by calling CallProcess.getInstance() (there is one instance shared by all threads). Again an example from the MassInvoicing module, calling the C_Order_Post process for a list of invoices:

// get an AD_Process instance, 111 is the C_Invoice_Post process
final org.openbravo.model.ad.ui.Process process = OBDal.getInstance().get(org.openbravo.model.ad.ui.Process.class, "111");

// iterate over the invoices and post them
for (Invoice invoice : invoices) {

final ProcessInstance processInstance = CallProcess.getInstance().call(process,
invoice.getId(), new HashMap<String, String>());
// the processInstance now contains the result
final String errorMsg = processInstance.getErrorMsg();
final Object result = processInstance.getResult();
final String recordID = processInstance.getRecordID();
// code truncated for clarity

}


The last parameter in the CallProcess.call method is a map of parameters, these are placed in the ad_pinstance_para table.

The CallStoredProcedure class makes it possible to call any stored procedure in the database in a java programmer friendly way. The nice thing of this class is that you can pass in actual java objects as parameters, so you don't need to worry about String conversions. The return is also a type-specific java object. This code snippet shows its usage (calling the C_Divide stored procedure):

// set some parameters
final List parameters = new ArrayList();
parameters.add(new BigDecimal("10.1"));
parameters.add(new BigDecimal("2.0"));
// the procedure name
final String procedureName = "C_Divide";
// calling the procedure and getting the result
final BigDecimal bigDecimal = (BigDecimal)CallStoredProcedure.getInstance().call(procedureName, parameters, null);

The last one to discuss: the OBObjectFieldProvider class makes it possible to wrap an Openbravo business object in a FieldProvider interface. The FieldProvider is used throughout the Openbravo system to wrap data read from the database (for example through a XSQL generated class). The OBObjectFieldProvider is useful when you read business objects through the DAL and need to pass them on to code expecting a FieldProvider.

The discussed classes are all documented with javadoc describing the meaning of parameters and methods and you can ofcourse study the source code directly.

Before concluding this blog, as a last tip: The DAL (Hibernate) queues actions to the database. So when updating the database through DAL and then calling a stored procedure using a DalConnectionProvider make sure to call OBDal.getInstance().flush() before calling the stored procedure. This flushes the queue to the database and ensures that the stored procedures sees your changes. Also when you want to work with the results from the stored procedure and read them back through the DAL read this tip.

As always I hope this is an interesting read, feedback is always welcomed. If you have any questions or remarks visit the Openbravo forge forum and post them there.

Happy Coding!

Module: Integrating JBoss Seam and Openbravo

donderdag 5 november 2009

The Openbravo Seam module integrates Seam with Openbravo, making it possible to develop Seam applications on top of Openbravo. The OB Seam module is a special module in that it provides custom implementations of specific Openbravo core (DAL) classes. This is a good example of the extendability provided by Openbravo modularity!

The OB Seam module implements two specific Seam/Openbravo integration points:

  • Session/Transaction handling
  • User context as a Seam component.
Session/Transaction handling: Openbravo uses a so-called open-session-in-view pattern (one Hibernate session per HTTP request), while Seam mainly uses an EntityManager (is equivalent to a Hibernate session) per conversation. The OB Seam module makes it possible to use these two concepts next to eachother. Openbravo Seam automatically detects that a Hibernate session is opened as part of a Seam conversation or independently from a Seam conversation. In the latter case the session-in-view pattern is automatically applied, in the first case Seam takes care of transaction handling.

The user context as a Seam component: the module makes the OBContext object available through the OBUserContext component. The module contains an example of a Seam authenticator which authenticates a user/password against the Openbravo user database, the authenticator adds the Openbravo role in the Seam identity object.

The OB Seam module is shipped with a small example app (developed with RichFaces/JSF) which allows a user to login with his/her Openbravo account and then view the content of all tables available for the current role of the user. The user can switch role to view the content of other tables. The table view is a paging grid with a detail view.

After installing the module (read the install tips here) and starting Openbravo you can reach the example app through this url:
http://localhost:8080/openbravo/web/org.openbravo.base.seam/page/show-entities.xhtml.
Make sure that the browser has enough horizontal space (the grid/detail are layed out horizontally) then login using your Openbravo login/pwd (for example: Openbravo/openbravo).

To help you getting started, there is a short developers manual available here.

The module is available through the central repository. I invite you to try it out, it should be easy to extend the example app. As always feel free to share your experience with us!

Update April 2010


To facilitate the download size, the original Seam module has been split in several modules:

  • Base Seam: the original Seam module now provides the main backend integration with Openbravo but no jsf/user interface parts.

  • Seam Richfaces: provides the JSF and Richfaces parts of Seam.

  • Seam Test: provides the Seam test environment.

Module: Openbravo JSON REST Webservice!

maandag 26 oktober 2009

As some of you may know we are currently prototyping a new user interface library. As part of the development for the new user interface we have developed a JSON REST webservice module.

This module is a very good example on how you can extend the Openbravo webservice functionality using Openbravo modularity. It shows how easy it is to extend Openbravo not only when coding your functionality but also when configuring your services (extending web.xml and other configuration files).

JSON is a light-weight data-exchange format used extensively for client-service communication in web based applications. The JSON REST module is similar to the standard Openbravo XML REST webservice in that it makes Openbravo information available through a webservice supporting CRUD operations.

The module can be downloaded from the forge here or can be installed from the central repository. After installing the module and restarting Openbravo you can try out the webservice directly. For example enter the following url in the address-bar of the browser:

http://localhost:8080/openbravo/org.openbravo.service.json.jsonrest/Country/100

This call will retrieve the Country business object with id 100 as a JSON string. You can save the returned content and open it in a text editor. The JSON will look like this:

{"_identifier":"United States","entityName":"Country","$ref":"Country\/100","id":"100","client":{"_identifier":"System","entityName":"ADClient","$ref":"ADClient\/0","id":"0","active":true},"organization":{"_identifier":"*","entityName":"Organization","$ref":"Organization\/0","id":"0","active":true},"active":true,"creationDate":"2009-10-26T19:20:06","createdBy":{"_identifier":"System","entityName":"ADUser","$ref":"ADUser\/0","id":"0","active":true},"updated":"2009-10-26T19:20:06","updatedBy":{"_identifier":"System","entityName":"ADUser","$ref":"ADUser\/0","id":"0","active":true},"name":"United States","description":"United States of America","iSOCountryCode":"US","hasRegions":true,"regionName":"State","phoneNoFormat":"Y","addressPrintFormat":"@C@, @R@ @P@","additionalPostalCode":false,"default":true,"language":{"_identifier":"English (USA)","entityName":"ADLanguage","$ref":"ADLanguage\/192","id":"192","active":true},"currency":{"_identifier":"USD","entityName":"Currency","$ref":"Currency\/100","id":"100","active":true}}

Each of the properties of the Country object is present for example. client, id, iSOCountryCode etc. Openbravo JSON REST adds some special properties: _identifier and $ref which can be useful on the client. Each foreign key reference is also represented in a special way. for example the Country refers to the English (USA) language, the reference to that language is represented like this in the JSON string:

"language":{"_identifier":"English (USA)","entityName":"ADLanguage","$ref":"ADLanguage\/192","id":"192","active":true}

the 'language' is the property of the Country then within brackets the information of the language object itself is present. Adding this information in the returned JSON string has as advantage that it is not needed to do extra requests from client to server to display all the details of the Country object (including referenced objects).

The JSON REST service also supports query and paging parameters, some examples:


See the JSON REST Developers Manual for more details on the querying capabilities and on the format of the returned information. The JSON REST module uses the same authentication and authorization functionality as the XML REST webservice functionality and the data-access-layer.

The JSON REST service also supports insert, update and removal of information. To insert a new Country you have to post a message like shown below to the Openbravo application and use the POST HTTP method. To try this directly in your browser you can make use of a Firefox plugin like poster.

{data: {"entityName":"Country","active":true,"name":"Test","description":"Test Country","iSOCountryCode":"ZZ","hasRegions":true,"regionName":"State","phoneNoFormat":"Y","addressPrintFormat":"@C@, @R@ @P@","additionalPostalCode":false,"language":{"id":"192"},"currency":{"id":"100"}}}


You see the result below. The system replies with a response JSON object which contains all the data of the inserted Country. The important one to take note of is the id.


We will be using the id in the next step to update the name of the Country to a better one (if you try this your self, the id will be for sure different!).

{data: {"entityName":"Country","id":"FF80818124923C2C0124923D39A80002","name":"My Great Test Country"}}

The response will return the complete JSON object with a status code (0 for success).

Then to clean up, let's delete the newly created country. Use this url (replacing the id part with your id) and do a DELETE HTTP method request:

http://localhost:8080/openbravo/org.openbravo.service.json.jsonrest/Country/FF80818124923C2C0124923D39A80002

A response with status 0 is returned with the deleted object as the data entry. This clean up action concludes this short tutorial. I hope you found it interesting.

As always we welcome feedback. We will be using this module as part of our new Openbravo user interface development and extend it further based on new requirements.

Selecting a new User Interface Framework/Library (comparing dojo, extjs and smartclient)

woensdag 16 september 2009

Openbravo is planning to re-design and re-implement the current user interface using both new user interface design paradigms as well as using new technology in a different architecture. The user experience re-design is being done by Rob Goris with some great results and a lot of response from the community.

On the more technical side we are currently in the process of selecting a new user interface framework/library. As many people have noted it is a difficult task to select a user interface framework. There are many good frameworks out there and also many people with strong opinions on what framework is the best. In the end of-course the main deciding factor is if a framework fits to the specific requirements of a web-based ERP.

Next to the obvious selection criteria (open-source, mature, large community, cross-browser, etc.) there are also Openbravo specific selection criteria which play a role in the process:

  • a framework should support our target architecture
  • a framework must work in a model-driven architecture with runtime re-generation of the user interface (in our new approach the user interface should be re-generated without restarting the application)
  • a framework should preferably be targeted for data-oriented web ui's (powerfull grids/forms/tabs play a vital role in these types of user interfaces).
We have decided to do this selection process in three steps:
  1. define a long list of user interface libraries
  2. select a shorter list based on specific selection criteria
  3. get the list down to a small number libraries and do a more detailed comparison and analysis
For the long list we considered a total of 22 different frameworks (see the complete list here). In the first cycle we cut down the long list to four, using these selection criteria. Based on further analysis and also some community feedback we decided on our final short list: dojo, extjs and smartclient.

At the moment we are in the last stage of the selection process and are doing a more thorough analysis of both dojo, extjs and smartclient. The process and conclusions can be found here (this page is under construction). The detailed analysis is being done by building targeted prototypes and studying available information such as the documentation, sample code, forum posts, tutorials and other information sources.

As always we are interested in your feedback. Feel free to check out this page and to give your own opinion and experience in this forum post!

Openbravo 2.50: The Developers Guide

donderdag 27 augustus 2009

Openbravo 2.50 has been released a few months ago, so some of you may have already noticed that as part of this release we created a completely new developers guide. This blog is to help spread the word even further that the developers guide is a great resource of information available for those of you who are customizing or developing on Openbravo 2.50.

The developers guide consists of different sections providing valuable information for starting developers, medium experienced as well as Openbravo expert developers. Here is an overview of these sections.

The introduction is relevant when starting development with Openbravo (see the Development Environment and Development Concepts sections) and for developers wanting to know more about the changes from 2.40 to 2.50 (see what's new and howto upgrade).

The how-to sections describe Openbravo development topics using a focused development goal. There are how-tos on modularity, extending the datamodel, adding windows and webservices, the data access layer and more.

The fundamentals and concepts chapter gives a detailed description of all relevant Openbravo ERP development concepts. The content ranges from common development topics (such as the project structure, build tasks) to modularity and the application dictionary. Each of the main layers of the application is discussed: database, middle-tier and web-tier with webservices.

The reference section consists of a detailed description of the data model from different points of view: the database model, the entity model, the hibernate mapping and the REST XML Schema. In addition javadoc and other reference-like topics are discussed in this section.

The examples section contains examples of existing code in the Openbravo ERP application. The purpose of this chapter is to give directions for the reader to study current Openbravo code and use that as the basis for own custom code

A very useful section is the tips and tricks section. The tips and tricks are based on experience and user questions and solutions in the forums.

I hope that the developers guide is useful for you as a developer. Feel free to give feedback on the guide, especially if certain parts can be improved or if you miss certain information.

Happy reading!

Openbravo 2.50: Using the Model in your Application Code

dinsdag 28 april 2009

As you may know Openbravo follows a Model-Driven-Development (MDD) approach. The main theme of MDD is generating an application from a model. An aspect which is often ignored by traditional MDD methods is the availability of the model at runtime. Openbravo specifically pays attention to the runtime model and makes it available through an easy-to-use api.

The main advantage of a runtime-model is that it is possible to develop very generic application functionality at model-level. Writing code at model-level is far more efficient than writing specific code for each table. Another advantage of model-level code is that it is often robust for model changes.

Here are some examples of generic application functionality which can make use of the runtime model:

  • security
  • import and export logic
  • archiving
  • tracking/tracing of changes
Some main concepts of the Openbravo runtime model:
  • The runtime model consists of a set of Entities.
  • An Entity is a domain concept like an Order or a Product. Currently in Openbravo an Entity is the same as a table (each table has an Entity representing it and vice versa).
  • An Entity has a list of Properties, for example an id, a name, a description. Some properties are primitives (string, number, etc.), some are single references (from an Order to a Currency), some are many references (from an Order to all its OrderLines).
To make use of the runtime model it is good to understand the Openbravo business object class model.


As you can see, each business object (order, product, etc.) inherits from the BaseOBObject class. The BaseOBObject class offers a number of important methods:
  • getEntity(): to get access to the domain concept (=table) represented by this java class
  • get(String propName): get the value of a certain property
  • set(String propName, Object value): set the value of a certain property
  • getIdentifier(): returns a readable name for the business object
So while the specific subclass has specific set and get methods for each property, the main super class has generic set and get methods to set/get the value of all properties of a business object.

In a runtime model-driven-development approach the code uses the runtime model and accesses objects as a generic BaseOBObject. In Openbravo 2.50 access to the runtime model is provided by the ModelProvider class.

I will illustrate the use of the ModelProvider class and the Openbravo runtime model in general with some example code. This sample code exports all data in an Openbravo instance in just a few lines of code, the code executes the following steps:
  1. get all Entities from the ModelProvider
  2. for each Entity get all records/instances from the databases
  3. for each instance iterate over its properties and read the property value
  4. create a string representation of that property value
// as we read all entities, be an administrator to prevent
// security exceptions
OBContext.getOBContext().setInAdministratorMode(true);

// iterate over all entities
for (Entity entity : ModelProvider.getInstance().getModel()) {

// query for all objects of the entity and iterate over them
final List businessObjects = OBDal.getInstance().createCriteria(
entity.getName()).list();
for (BaseOBObject businessObject : businessObjects) {

final StringBuilder line = new StringBuilder();

// place the entity name so for each line it is known what type is exported there
line.append(entity.getName());

// and iterate over all the properties of the entity
for (Property property : entity.getProperties()) {
// ignore these type of properties, as the children are exported separately
if (property.isOneToMany()) {
continue;
}

line.append(SEPARATOR);

// get the current value
final Object value = businessObject.get(property.getName());
// handle null
if (value == null) {
continue;
}
// export primitives in the same way as xml primitives
if (property.isPrimitive()) {
line.append(XMLTypeConverter.getInstance().toXML(value));
} else {
// export the id of a referenced business object
line.append(((BaseOBObject) value).getId());
}
}
writer.append(line + "\n");
}
}
The data is appended to the writer object (which can be a FileWriter), with the BigBazaar Openbravo sample data this results in a file of about 13mb. The XMLTypeConverter is an Openbravo class which correctly converts primitive types such as a Date, a number, etc. to a String.

You can copy the above code directly in a test case and run it (set the writer and SEPARATOR variables). For how-to create a test case in Openbravo see this how-to.

An additional nice feature of the Openbravo runtime model is that all tables added by modules are automatically made part of the runtime model. So for Openbravo in the runtime model, there is no difference between an Openbravo core Entity/table or a custom module table/Entity.

The runtime model is used extensively by the Data Access Layer and REST web services.

Well I hope that this blog gives some inspiration to try out coding at model level and to make use of the Openbravo runtime model. Thanks for reading, and feel free to ask any (detailed) questions on the Openbravo deverlopers forum!

Openbravo 2.50: REST Webservices

maandag 2 maart 2009

In this post I will talk about new very exciting functionality in Openbravo 2.50: full REST web services support for all tables in the Openbravo datamodel (including the tables added by modules).

I will first start with a general overview and then some examples of web service calls which you can try directly in your browser. The post is concluded with a short description on how to add your own REST-like web services and a number of interesting links on REST.

Openbravo REST provides a CRUD-like interface so that external applications can retrieve, update, create and delete business objects through standard HTTP requests.

Some benefits of using a REST approach:

  • favors identifying and addressing resources which fits to the data-centric nature of the provided apis (a resource corresponds to a business object)
  • has actions (POST, PUT, DELETE, GET) which correspond to standard CRUD actions
  • allows linking to specific business objects or to sets of business objects. This is a very powerfull feature of a REST approach and it allows for easy navigation between business objects.
  • is simple to develop and use, and very lightweight from an architectural point of view
The Openbravo REST webservice operates on Business Objects in Openbravo. Before continuing let's first explain what a Business Object is (in Openbravo). A business object can be a simple entity (==table) such as a currency which just has basic primitive fields. On the other hand it can also be a structure of entities, for example an order header with its order line.

Openbravo REST web services provide the following functionality:
  • retrieve a single business object or a list of business objects using a standard HTTP GET request
  • querying, filtering, paging and sorting of lists of business objects, again through standard HTTP requests
  • update of an existing business object or multiple business objects through XML and a HTTP POST or PUT operation
  • creation of new business objects through a POST/PUT operation
  • export and import of data: xml files which contain a mix of different types of business objects and a mix of new and existing business object
  • delete operation using either a url pointing to a specific business object which needs to be removed or a XML document which contains business objects (as full xml or as partial xml) which need to be removed.
This functionality can be used for standard integration scenario's, but it can also be used to develop another UI on top of Openbravo using an alternative UI-technology (e.g. Flex).

The Openbravo REST web services use the same access/authorizations as the standard Openbravo application. Before calling a web service the caller must log in. The login functionality is provided by the Openbravo REST framework. All REST actions are then executed in the context of a client/organization and current role of the user.

Now let's go to some examples. When you have Openbravo running then you can try these out directly in your browser by entering the urls in your browser's address bar. Note that the examples assume that Openbravo runs locally on port 8080, it maybe necessary to replace the localhost:8080 part with your own server name/port. The examples assume that the web service user has access to the Country and Currency tables.
  • Query for all Countries:
http://localhost:8080/openbravo/ws/dal/Country
  • Get a specific Country (in this case Spain):
http://localhost:8080/openbravo/ws/dal/Country/106

Note that the xml returned contains both the Country and its children (Regions), i.e. a business object structure.
  • An ordered example, query for all countries and return them ordered by ibanCode and regionName:
http://localhost:8080/openbravo/ws/dal/Country?orderBy=iBANCode,regionName
  • The same example with paging, returns 10 Countries starting from the 19th:
http://localhost:8080/openbravo/ws/dal/Country?orderBy=iBANCode,regionName&firstResult=19&maxResult=10
  • Do some filtering, only return countries which have a Currency with id 102 and a iBANLength of minimum 23:
http://localhost:8080/openbravo/ws/dal/Country?where=currency='102' and iBANLength>=23

(the where parameter can contain a Hibernate Query Language where clause)

After trying some examples, the next question is which web services are provided by Openbravo, i.e. what url's are valid, what are the entity names and XML property names, what is valid xml? To answer this question Openbravo REST has a special web service which can be called. This web service generates a XML Schema of the available business objects and their elements (including the tables added by custom/external modules). You can try it yourselve on your local running Openbravo instance:
http://localhost:8080/openbravo/ws/dal/schema

These first examples only retrieved data. The REST web services also have update/create/delete functions. To support web service testing, Firefox has a nice add-on: Poster. This add-on allows you to POST/PUT XML to a URL. For these examples I again assume that you have Openbravo running locally. I will be creating a new currency, updating its precision and then deleting the currency.

Here is an example of xml which can be used to create a new Currency:

<?xml version="1.0" encoding="UTF-8"?>
<ob:Openbravo xmlns:ob="http://www.openbravo.com">
<Currency>
<active>true</active>
<iSOCode>OBD</iSOCode>
<symbol>€</symbol>
<description>Openbravo Dollars</description>
<standardPrecision>2</standardPrecision>
<costingPrecision>4</costingPrecision>
<pricePrecision>4</pricePrecision>
<currencySymbolAtTheRight>true</currencySymbolAtTheRight>
</Currency>
</ob:Openbravo>

You can easily create this xml by retrieving a Currency through a url (for example, the euro) and then removing the XML parts related to id, client/organization and audit info.

Click on the Poster icon (right-bottom in Firefox) and set the options as displayed in the image below.

Note that the xml (displayed above) is entered in the Content field, the Action is set to POST and the User Auth. fields contain the login and password. The user must have permissions to create a Currency. The standard Openbravo demo user has these capabilities.

Then click on the first GO button, you should be seeing the following result:

This xml gives a success message but more importantly it also gives the id back of the newly created object. This allows software, making REST calls, to use this id in further processing. In our case we can use this id to check if the currency was indeed created (note replace the id in the url with the id you received back):
http://localhost:8080/openbravo/ws/dal/Currency/FF8081811FA6E26B011FA6EA2E9C0002

Now as a next step let's update a field of the new Currency, in this case the precision is changed. The image below shows how this is done. The xml only has the field which needs to be updated and the id of the Currency is present as an xml attribute (to try-this-at-home, replace the id value with the one created in your case).


And to clean up let's delete the new currency. This is done with a DELETE action, the url of the action needs to point to the business object which needs to be deleted (in this case the Currency created above).


The above actions can be performed for all of the 425+ tables in Openbravo. More importantly REST webservices automatically work out-of-the-box also for new tables added by modules.

The delete action concludes the quick overview of the capabilities of Openbravo REST Webservices. The overview hopefully showed how easy it is to use REST webservices. Software talking to REST webservices need basic xml processing capabilities but that's the only real prerequisite.

The Openbravo REST framework can be extended with new Webservices. See here for more information. Openbravo REST takes care of security and exception handling. Web services can be added (installed/uninstalled) as part of a module.

For more information:
  • REST Webservice Technical Design
  • REST test cases can be found in the openbravo development project in the src-test folder and then in the org.openbravo.test.webservice package
Here are some other interesting (non-Openbravo) links:

Some links related to REST versus SOAP, there is a fair amount of articles on the web on this topic: