Openbravo 3 and Mobile With Core Architecture
Openbravo 3 and Mobile With Core Architecture
0 Architecture
In Openbravo 3.0 the Openbravo ERP system moves away from a classic servlet architecture to a Rich
Internet Application (RIA) architecture. This is a radical paradigm shift resulting in a much more dynamic
and versatile user interface. In a RIA architecture the requests for data and for the page/user interface are
separated. The page/user interfaces is loaded on the client once and then re-used while the user works in
the application. This in contrast for requests for data and actions, these are executed continuously.
The Openbravo 3.0 architecture is implemented using Openbravo Core and several related modules:
The JSON module provides the JSON REST web service, it is used for the client-server data
communication
The Weld module provides dependency injection and component management
The Kernel module takes care of infrastructure tasks as request processing, event
handling, compression and caching
The DataSource module uses the JSON module and provides higher level data request functionality
for querying and data related actions
The Smartclient module provides the Smartclient user interface library
The application module contains the implementation of the navigation bar, grids and forms and
other application oriented client and server side code.
This guide explains the concepts implemented by these modules.
Components are implemented in modules. A module has complete freedom on how to implement
components (if there is a window table, a grid table or a generic view table etc.). The components within a
module are managed by a Component Provider, it is responsible for creating a component and providing it
to the Openbravo kernel. A component provider is also responsible for registering the static content with
Openbravo, this is explained in more detail below.
A component can be retrieved using a URL (a component request). For example this URL will retrieve the
javascript for the sales invoice window:
http://localhost:8080/openbravo/org.openbravo.client.kernel/OBUIAPP_MainLayout/View?viewId=_167
To explain the structure of this url, the OBUIAPP_MainLayout identifies the client application module. The
ComponentProvider of that module knows how to handle the last part of the url: View?viewId=_167. It will
create a StandardWindowComponent which gets the viewId (the ADWindow id) to generate the javascript.
The generated javascript is postprocessed (compressed, syntax checking) before it is send to the client.
The following image illustrates the request flow processing in more detail:
The overall request and generate flow operates as follows:
1. a component request (with a component type and id) is received by the Client Kernel module (in
the KernelServlet).
2. based on the component type the kernel module finds the module responsible for handling the
requested component, that module is then called to create the component using the component id.
3. the module reads the component definition from tables or other sources and instantiates the
Component instance.
4. the component is called to generate its client-side representation.
5. if it is a templated component (i.e. it has a specific template) then the component calls a template
processor for the template.
6. the view is generated using the template or by executing javacode which generates a String
(javascript).
7. the result (often a javascript string) is returned to the Client Kernel module.
8. the Client Kernel module compresses the result using jsmin.
Note the Client Kernel module also takes care of caching components and component requests. This is
covered in a later section in this documentation in more detail.
Core parts of the Openbravo architecture use Weld for dependency injection, component definition and
business entity events. The starting point is defining components and the scope in which they live. The next
step is using these components by injecting them in other components. Note, to make use of Weld
dependency injection it is not possible to create objects using the classic java new keyword. See a later
section below on this topic.
Starting from 3.0PR19Q3, Openbravo supports CDI 2.0 as Weld was upgraded to version 3.1.0.
Scope Definition
With Weld it is possible to define components which are available at different levels: ApplicationScoped,
SessionScoped and RequestScoped. For each of these scopes examples can be found in the Openbravo
source code. Scoping is defined using an annotation:
@ApplicationScoped
@ComponentProvider.Qualifier(ExampleComponentProvider.EXAMPLE_VIEW_COMPONENT_TYPE)
public class ExampleComponentProvider extends BaseComponentProvider {
...
}
@SessionScoped
public class MenuManager implements Serializable {
....
}
Note, the above annotation for ComponentProvider is an Openbravo specific annotation used to register
ComponentProvider globally using a unique string. See the section on ComponentProviders for more
information.
Scope Inheritance
Regarding the inheritance of scope annotations, Weld follows the expected behavior described on CDI 2.0
spec.
Suppose a class X is extended directly or indirectly by another class Y. If X is annotated with a scope
type Z then Y inherits the annotation if and only if Z declares the @Inherited meta-annotation and neither
Y nor any intermediate class that is a subclass of X and a superclass of Y declares a scope type. Note that
this behavior is different to what is defined in the Java Language Specification.
Therefore, as all @ApplicationScoped, @SessionScoped and @RequestScoped annotations declare
the @Inherited meta-annotation, a subclass inherits the scope of its parent class if neither the subclass
nor any intermediary class declares another scope.
This behavior does not apply to those classes implementing an interface with a particular scope: if an
interface declares an scope, it is not inherited by the classes implementing that interface.
Injection
The defined components can be injected automatically in other components using injection points which
are defined using the @Inject annotation:
@Inject
private MenuManager menuManager;
Assignability Rules
It is important to read this section before upgrading to 3.0PR19Q3 release.
CDI 2.0 defines under which circumstances the assignability of raw and parameterized types works within an
injection point. This is specified with a set of rules defined here.
The Weld version used in versions prior to 3.0PR19Q3 was not adhering to these rules and thus it allowed to
define some injection points which are no longer valid starting from 3.0PR19Q3. For example, given the
following class:
// Before 3.0PR19Q3
@Inject
@Any
private Instance<DocGenerator<BaseOBObject>> docGenerators;
...
}
Starting from 3.0PR19Q3 we need to use wildcards to define the injection point in order to follow the
assignability rules:
// After 3.0PR19Q3
@Inject
@Any
private Instance<DocGenerator<? extends BaseOBObject>> docGenerators;
...
}
org.openbravo.base.weld.WeldUtils.getInstanceFromStaticBeanManager(Class<T> type);
The created instance will be integrated with Weld and can make use of its dependency injection capabalities,
also the scope defined for the component is taking into account. So when calling the above method for an
ApplicationScoped component, always the same single instance is returned.
As the default Weld searches in all the class files in WEB-INF/classes but as a default the jar files in WEB-
INF/lib are excluded from this search. See here (Packaging and Deployment chapter of the Weld
documentation) on how to create a jar file which is searched by Weld.
Openbravo Weld will also exclude specific classes from WEB-INF/classes. This to prevent searching all
classes specific exclusion filters have been specified. They can be found in the config/beans.xml file in the
Weld module. The classes which are caught by the exclusion filter will not be considered as components and
will not be found by Openbravo/Weld.
The Weld Development Mode is an special mode that provides several built-in tools suitable for
development and testing purposes. By default this mode is disabled. Weld Probe is one of these tools.
The org.openbravo.base.weld.dev module can be used to enable the Development Mode. This module
should never be used in productive environments because it may have a negative impact on
performance.
Openbravo implements several types of caching in different layers of the application to optimize the user
experience. It takes advantage of the modularity functionality, meaning that Openbravo makes a distinction
between a situation when a module is in development or not. For a module in development it makes sense
to prevent caching as a consultant changing the user interface wants to get instant feedback. For a module
not in development it makes sense to maximize caching as this improves the user experience. When a
module is not in development then the module version is used to automatically refresh the client cache.
Static js files are normally cached in the browser. Static js files may however change during module
upgrades or development.
This is implemented as follows. Static js files (i.e. global resources) are provided by modules. A module
publishes its static resources through a ComponentProvider. The api of a ComponentProvider contains a
getGlobalResources method. Openbravo concatenates all static resources into one large js file. The
concatenation order is based on the dependency relations between the modules. The name of the js file is
based on a guid (for example: 088afd247a8fe06c91a654891a1358a2.js). This guid is again based on the
content of the file, so if the javascript changes then also the js file name is changed (and therefore reloaded
in the browser). The js file is generated into the web/js/gen folder and served from there to the client.
Note: the concatenated javascript file will not be compressed if the core, client.kernel and client.application
modules are in development. This facilitates client side debugging.
To support the concept of server side cache expiry the Client Kernel makes use of the ETag concept. An
ETag is like a hashcode which is used to determine if content has changed since the last time the browser
requested it. The BaseComponent implementation generates ETags in two ways:
if the module of the component is not in development then the ETag is: the concatenation of the
language of the user and the version of the module
if the module of the component is in development then the ETag is: the concatenation of the
language of the user and a timestamp in milliseconds
This gives the same result as with static content: if a module is not in development client side code is only
refreshed when a module is upgraded, when a module is in development client side code is not cached. This
last behavior is very desirable when changing component definitions (adding columns, renaming fields etc.)
as the system does not need to be restarted or the browser cache emptied to see the results.
Note: a Component can contain other components provided by other modules. The generation of the ETag
only takes into account the indevelopment state of the module of the root Component.
Starting from PR19Q3 JSLint is replaced by ESLint and this check is not performed in this step.
The output generated by a module can be compressed and syntax checked. For compression jsmin is used.
For syntax checking Openbravo makes use of JSLint and JSLint4Java. The compression/syntax checking is
done depending on the in development status of a module:
if a module is in development then the output of its components are syntax checked but not
compressed (to enhance readability for a developer in the browser)
if a module is not in development then the output is not syntax checked but it is compressed (to
enhance performance for endusers)
http://www.infoq.com/articles/etags
http://www.oreillynet.com/onjava/blog/2004/07/optimizing_http_downloads_in_j.html
http://www.xml.com/pub/a/2006/02/01/doing-http-caching-right-introducing-httplib2.html
http://bitworking.org/news/150/REST-Tip-Deep-etags-give-you-more-benefits
http://blogs.atlassian.com/developer/2007/12/cachecontrol_nostore_considere.html
http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13
the java source code and templates are located in the src directory
the static javascript should be located in this subdirectory: web/[modulepackage]/js
the styling artifacts (images, css and javascript) should be located in this subdirectory:
web/org.openbravo.userinterface.smartclient/3.00/[modulepackage]
the javascript unit tests should be located in the web-test directory.
By following this structure the files will be copied to the correct location during the main build steps.
Component Provider
Each module needs to implement a component provider. A component provider takes care of the following
tasks:
it creates the components of the module on request from the Openbravo kernel
it registers the static resources of the module into the Openbravo kernel.
A ComponentProvider is a Weld component and has a standard structure which is explained here.
@ApplicationScoped
@ComponentProvider.Qualifier(ExampleComponentProvider.EXAMPLE_VIEW_COMPONENT_TYPE)
public class ExampleComponentProvider extends BaseComponentProvider {
public static final String EXAMPLE_VIEW_COMPONENT_TYPE = "OBEXAPP_ExampleViewType";
The component provider must always be in the same root package as the package of the module it is
in. The developer must NOT put it into a subpackage of the main module package!
Note: implementing this method is only needed if you have dynamic content, if the module only has static
js/css files then this method can remain empty (or just throw an exception).
A component can be very simple, it only needs to implement the generate method if it extends the
Openbravo BaseComponent class. A very simple example of the implementation of the generate method:
public String generate() {
return "alert('Hello world!')";
}
A very important task of the component provider is to register static resources: javascript and css. These
static resources are concatenated/compressed by the Openbravo kernel:
public List<ComponentResource> getGlobalComponentResources() {
final List<ComponentResource> globalResources = new ArrayList<ComponentResource>();
globalResources.add(createStaticResource(
"web/org.openbravo.client.application.examples/js/example-view-component.js", true));
globalResources
.add(createDynamicResource("org.openbravo.client.kernel/"
+ ExampleViewComponent.EXAMPLE_VIEW_COMPONENT_ID + "/"
+ ExampleViewComponent.EXAMPLE_VIEW_COMPONENT_ID));
globalResources.add(createStyleSheetResource(
"web/org.openbravo.userinterface.smartclient/openbravo/skins/"
+ KernelConstants.SKIN_VERSION_PARAMETER
+ "/org.openbravo.client.application.examples/my-styles.css", false));
return globalResources;
}
The code above shows how to register javascript and css resources. Also an example of a dynamic resource
is shown, this will call the component to generate javascript.
On the server the logic has to be implemented in a class which implements the ActionHandler interface.
....
// and return it
return json;
} catch (Exception e) {
throw new OBException(e);
}
}
}
This server side can be called from the client using this javascript:
// define the callback function which shows the result to the user
var callback = function(rpcResponse, data, rpcRequest) {
isc.say('The result is : ' + data.result);
};
HowTo
This HowTo contains an example of a server-side ActionHandler which is called from the client.
Implement Application Initialization Logic
In modules it sometimes makes sense to initialize something when the application starts. Openbravo
provides a mechanism to implement application initialization logic. You have to do the following:
@ApplicationScoped
public class KernelApplicationInitializer implements ApplicationInitializer {
Weld will automatically find your class and Openbravo will call it when initializing the data access layer. Note
that you have access to the SessionFactory through the SessionFactoryController but not to a Hibernate
Session. So for accessing the database you have to create a Hibernate session from the SessionFactory.
Note also: if your application code is not being called, then check out this tip.
Business entity events are broadcasted on the event, so before the event is executed in the database. It is
possible to change the relevant entity instance or do other database actions. The event handlers run in the
same transaction as the business event itself.
Note: Business entity events only work when accessing the database through the data access layer, so
they do not work for classic windows or direct jdbc calls!
How To
This how to gives a detailed description on how to implement your own event handler listening to business
entity events.
EntityNewEvent
EntityUpdateEvent
EntityDeleteEvent
TransactionBeginEvent.java
TransactionCompletedEvent.java
The event classes have javadoc which gives more details on their api.
The most relevant apis are discussed here:
getTargetInstance (available for all events): returns the business object which is the target of the
event, note: do not call set methods on this object directly, the new values are not
persisted as Hibernate has already read the values from the object
setCurrentState(Property property, Object value) (available for all events but only relevant for
update/new events): sets a new value for the property on the target instance, the new value will be
persisted and then the target instance is updated. The Property can be retrieved from the entity of
the targetInstance (call getEntity on the target instance and then getProperties or getProperty)
Object getPreviousState(Property property) (only available in the update event): returns the
previous value (currently set in the database) for the property in the entity
Openbravo provides and implements several client side components for sending requests to the server,
internationalization etc. See the Client_Side_Development_and_API for more information.
Smartclient
Openbravo uses the Smartclient client side user interface library for implementing components. To work and
development in Openbravo and create new widgets you need to understand Smartclient. Check out these
resources:
Smartclient demo
Smartclient forum
Smartclient reference
It can help to have the Smartclient source code and reference material in your Eclipse workspace, it is very
easy to search source code then. The latest Smartclient source code is available in this Mercurial
repository: https://code.openbravo.com/erp/mods/org.openbravo.userinterface.smartclient.dev/
Mobile Architecture
Openbravo Core Mobile Infrastructure is an Openbravo module which extends the Openbravo Platform with
HTML5-based mobile UI capabilities.
It provides an infrastructure that allows for the development of mobile web applications on top of
Openbravo. Openbravo mobile web applications are optimized for mobile devices and touch screens, and can
be offline-capable.
Server side
As you can see in the previous image, the server side of Mobile Core is an extension (using module) of
Openbravo ERP. We have included some components to provide data and resources to the client side.
Component provider
Takes the responsability to serve resources to the client side. It can be extended by other modules.
Datasource
This components provides data to the client parts and process the data which come from client side
to be included in the ERP.
Client side
We will explain the different technologies that we have used to build Mobile Core and what is the proposal of
each one.
enyojs - http://www.enyojs.com
enyojs is a javascript application framework sponsored by HP. Using it we have created the
components which conforms the windows of Openbravo Web POS. Enyojs also provides a useful
event management tool which allow us to trigger an event to be handled by another enyo
component. One of the best things of Enyojs is the capability to extend or overwrite each
components, it help us to customize components using an Openbravo ERP module.
onyx
onys is a toolbox of UI enyo components. We use some of them to build our layout.
UI Components
based on the two libraries explained in the previous sections, we have created a set of common ui
components (such as buttons, menus, keyboard, etc.) to be reused by different applications
Layout
using onyx's Panels, we have created a multicolumn layout. Depending on the device resolution it
shows multi column or single column in this way the applications are usable even in small devices.
Backbone - http://backbonejs.org/
Backbone.js is a JavaScript framework that allows you to structure your JavaScript code in an
MVC(Model, View, Controller) paradigm. In our particular case we have used backbone to work with
data (models and collections). A model provide functionality for managing changes using its own
methods and event handlers.
Underscore - http://underscorejs.org/
Backbone depends on underscore. This library provides interesting tools to work with collections.
WebSQL - http://www.w3.org/TR/webdatabase/
This technology allow us to store big data in the client side and access to it easily and faster. This
data will be retrieved from server (thanks to datasource) and stored in WEBSQL database. When
the data is saved we can read it using dal.
Client DAL
DAL is one of the most important parts of Openbravo ERP. It helps to easily interact with database,
in fact, it is used in the server side components. To access to WEBSQL stored data a client side DAL
has been created. It allow us to manage WEBSQL database easily.
Client Datasource
Datasource is a concept which involves the tools that allow us to communicate with the server in
order to send or retrieve data easily from the client side.
Mobile Application
On top of Mobile Core Infrastructure module, mobile applications can be built.