Apache OFBiz Development : The Service Engine

August 26, 2009

Uncategorized

«»

Apache OFBiz Development

Apache Open For Business or OFBiz as it is more commonly known, is an open source
framework designed to facilitate the building of Enterprise Resource Planning (ERP)
software. ERP is a general name for any system which attempts to integrate all business
processes and underlying data into one single system. Indeed the OFBiz framework not
only facilitates the building of your own custom software, but also comes packaged with
many tools you would expect from an ERP system, and much more. The extent to which
you wish to use these applications is entirely up to you and the needs of your business.
Some businesses choose to use some or all of these components virtually straight out of
the box. Others may spend time and money customizing these components or building
new ones to suit their own needs and their own unique business processes. Since OFBiz
is licensed under the Apache License Version 2.0, organizations can use, customize,
extend, modify, repackage, and even resell OFBiz completely free of charge.

OFBiz is aimed primarily at ecommerce businesses, giving easily customizable tools such
as a full Warehouse Management System (WMS), an accounting system and full order
and product management systems. It even has a full front end, customer facing website
and shopping cart with tools and features comparable to multimillion dollar websites such
as Amazon, not to mention its own set of self maintenance and administrative tools. Out
of the box, OFBiz is a multi-currency system working just as well with British Pounds,
Euros, or any other currency as it does with US Dollars. It is multilingual and is able to
display text in different languages depending on where in the world the user or customer
is looking. It is so versatile it is not even tied to one database, and fully supports most
well known databases.

The main reason for its versatility and size has been its open source model. OFBiz is truly
a collaborative effort with a small number of committers who have volunteered to
develop and maintain a code base supplied by both themselves and a growing
community. Although documentation on the tools is often thin on the ground (this is
mainly because of the speed at which the project and components evolve), there are free
and active mailing lists set up that will become an invaluable learning tool and source of
information as you progress with OFBiz. The OFBiz project employs the use of the well
known JIRA application (a bug and issue tracking and project management tool – which
is using the OFBiz Entity Engine, a major part of the framework). This allows developers
and users to tell the community about any bugs they find in the software or request new
features that they would find handy but perhaps don’t have the resources to develop for
themselves. Who knows? Once you have read this book you may even want to have a go
at developing an outstanding issue or fixing a bug for the project yourself!

What This Book Covers

Chapter 1, using a Windows machine, guides us through downloading and installing the
necessary software we need to obtain and run the OFBiz framework. We are shown how
to create an Eclipse project and once running we are shown a few of the components as
we place an order on the applications customer facing website and fulfill the order using
the back office’s Order Manager component.

In Chapter 2 we learn the structure of OFBiz. We are introduced to the concepts of
the framework, applications, and hot-deploy directories. We perform our first
customization on an existing OFBiz component and finally create the structure of our
own bespoke application.

In Chapter 3 we take a look at how the output to the screen is constructed using the
screen widgets. We start by creating a simple screen in our learning component, showing
a basic output. By the end of the chapter we have learned how to create complex screens,
made up of different sections.

In Chapter 4 we study form widgets. We learn how they are used within screen widgets
and can save us development time and effort by quickly producing XHTML forms so we
can input information to the application.

In Chapter 5 we complete our investigation into the presentation layer of OFBiz by
learning how to use Menu-Widgets to navigate around our component. We also take
more of a look at how FreeMarker can help us display more complicated screens.

In Chapter 6 we re-visit the Control, learning more about how OFBiz makes use of the
Front Controller pattern to configure the flow through our component in just one place.
We learn how OFBiz handles different types of requests and we are introduced to the
concept of security. By the end of the chapter, we have added “log in” functionality to our
bespoke application and have seen how easy it is to force a request to be “secure”.

In Chapter 7 we move on to the concepts of the Entity Engine and learn how OFBiz
employs the use of the Delegation Pattern to give us easy access to methods to persist
data. We learn how OFBiz creates the database structure, adding fields, tables, constraints
and indexes from definitions in XML files. We see how, by using View Entities, we can
perform joins across tables allowing us to create complex queries. We are also introduced
to the Webtools administration component of OFBiz and discover how to access the raw
data through these screens.

In Chapter 8 we are led through a series of examples designed to showcase data lookup
and persistence techniques. We learn how to use the GenericDelegator’s methods to
lookup and manipulate the underlying data. We discover how using the Entity Engine
Cache can massively improve performance by cutting down the number of database
queries and learn how complex queries can be created on the fl y by using Dynamic View
Entity. Finally we learn how to use the EntityListIterator to efficiently paginate through
large record sets.

In Chapter 9 we take a closer look at Java events by learning a number of techniques
vital to programming the flow of the application. We also take a look at how we
can assign users permissions and how these permissions are checked within the
Java methods.

In Chapter 10 we next see another type of event and a very important one—the services.
We learn about the advantages of the Service Engine and how it works, learning how to
define and write services in Java. We learn the difference between invoking these
services synchronously and asynchronously and how services can be scheduled using the
Job Scheduler. Finally we learn how to trigger these services using ECAs (Event
Condition Actions).

In Chapter 11 we move on to study complex permissions, learning how to assign users
granular permissions, and how simply these permissions can be checked from our
services. We learn by example how to restrict users from viewing or inputting data
depending on access rights whilst building up our bespoke application.

In Chapter 12 we learn about the OFBiz Mini-Language. We see how we can
write simple services and events in Minilang, and we learn when it should be
ideally used. We see its versatility and see how widespread its concepts are used
throughout the framework.

In Chapter 13 we come towards the end of learning about the framework, we see how
easy it is to change the look and feel of the component and study the structure of the
existing screens. The chapter moves on to some more advanced FreeMarker techniques
that are commonly used throughout all of the components. Finally using the production of
a PDF as an example we see how to output different formats.

In Chapter 14 we learn some real world developing techniques, including how to
debug through the different parts and languages found within the framework. We
see how to connect a remote debugger to the application and step through the
Java code line by line using the Eclipse IDE. We next learn the concepts behind getting
the latest bug fixes and features and merging these into our project using Windows tools
enabling us to successfully work with the latest and greatest version on OFBiz. Finally
we see learn how to run OFBiz behind the Apache HTTP Server allowing us to create a
scalable architecture.

The Service Engine

In this chapter, we will be exploring the Service Engine. Services in OFBiz operate in
a Service Oriented Architecture (SOA). These services not only have the ability to
invoke other services internally, but can also be ‘opened up’ and invoked by remote
applications using, amongst other methods, the widely adopted messaging
protocol SOAP.

Besides serving as a platform for interoperability, OFBiz services also offer us
additional capability to organize our code. The traditional organizational strategies
in object-oriented Java were a great improvement over the procedural paradigm.
Wrapping both methods and variables together into objects to form a powerful
“behavioral model” for code organization (where object’s methods and variables
define their behavior). Similarly with OFBiz services we are able to bundle groups
of behavior together to form a coherent “service”. We can say that OFBiz services,
in terms of code or software organization, operate at a higher level than Java objectoriented
organizational strategies.

In this chapter, we will be looking at:

  • Defining and creating a Java service
  • Service parameters
  • Special unchecked (unmatched) IN/OUT parameters
  • Security-related programming
  • Calling services from code (using dispatcher).
  • IN/OUT parameter mismatch when calling services
  • Sending feedback; standard return codes success, error and fail
  • Implementing Service Interfaces
  • Synchronous and asynchronous services
  • Using the Service Engine tools
  • ECAs: Event Condition Actions

Defining a Service

We first need to define a service. Our first service will be named
learningFirstService.

In the folder ${component:learning}, create a new folder called servicedef. In
that folder, create a new file called services.xml and enter into it this:


	<?xml version="1.0" encoding="UTF-8" ?>
	<services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/services.xsd">
		<description>Learning Component Services</description>
		<service name="learningFirstService" engine="java"
				location="org.ofbiz.learning.learning.LearningServices"
				invoke="learningFirstService">
			<description>Our First Service</description>
			<attribute name="firstName" type="String" mode="IN"	optional="true"/>
			<attribute name="lastName" type="String" mode="IN" optional="true"/>
		</service>
	</services>

In the file ${component:learning}\ofbiz-component.xml, add after the last
<entity-resource> element this:


	<service-resource type="model" loader="main"
					location="servicedef/services.xml"/>

That tells our component learning to look for service definitions in the file
${component:learning}\servicedef\services.xml.


	It is important to note that all service definitions are loaded at startup;
	therefore any changes to any of the service definition files will require
	a restart!

Creating the Java Code for the Service

In the package org.ofbiz.learning.learning, create a new class called
LearningServices with one static method learningFirstService:


	package org.ofbiz.learning.learning;

	import java.util.Map;
	import org.ofbiz.service.DispatchContext;
	import org.ofbiz.service.ServiceUtil;

	public class LearningServices {
		public static final String module =
		LearningServices.class.getName();
		public static Map learningFirstService(DispatchContext dctx,
		Map context){
			Map resultMap = ServiceUtil.returnSuccess("You have called on
			service 'learningFirstService' successfully!");
			return resultMap;
		}
	}

Services must return a map. This map must contain at least one entry. This entry
must have the key responseMessage (see org.ofbiz.service.ModelService.
RESPONSE_MESSAGE), having a value of one of the following:

  • success or ModelService.RESPOND_SUCCESS
  • error or ModelService.RESPOND_ERROR
  • fail or ModelService.RESPOND_FAIL

By using ServiceUtil.returnSuccess() to construct the minimal return map, we
do not need to bother adding the responseMessage key and value pair.

Anotherentry that is often used is that with the key successMessage
(ModelService.SUCCESS_MESSAGE). By doing ServiceUtil.returnSuccess(“Some
message”), we will get a return map with entry successMessage of value “Some
message”. Again, ServiceUtil insulates us from having to learn the convention
in key names.

Testing Our First Service

Stop OFBiz, recompile our learning component and restart OFBiz so that the
modified ofbiz-component.xml and the new services.xml can be loaded.

In ${component:learning}\widget\learning\LearningScreens.xml, insert a
new Screen Widget:


	<screen name="TestFirstService">
		<section>
			<widgets>
				<section>
					<condition><if-empty field-name="formTarget"/></condition>
					<actions>
						<set field="formTarget" value="TestFirstService"/>
						<set field="title" value="Testing Our First Service"/>
					</actions>
					<widgets/>
				</section>
				<decorator-screen name="main-decorator"
						location="${parameters.mainDecoratorLocation}">
					<decorator-section name="body">
						<include-form name="TestingServices"
								location="component://learning/widget/learning/LearningForms.xml"/>
						<label text="Full Name: ${parameters.fullName}"/>
					</decorator-section>
				</decorator-screen>
			</widgets>
		</section>
	</screen>

In the file ${component:learning}\widget\learning\LearningForms.xml, insert
a new Form Widget:


	<form name="TestingServices" type="single" target="${formTarget}">
		<field name="firstName"><text/></field>
		<field name="lastName"><text/></field>
		<field name="planetId"><text/></field>
		<field name="submit"><submit/></field>
	</form>

Notice how the formTarget field is being set in the screen and used in the form.
For now don’t worry about the Full Name label we are setting from the screen. Our
service will eventually set that.

In the file ${webapp:learning}\WEB-INF\controller.xml, insert a new
request map:


	<request-map uri="TestFirstService">
		<event type="service" invoke="learningFirstService"/>
		<response name="success" type="view" value="TestFirstService"/>
	</request-map>

The control servlet currently has no way of knowing how to handle an event of type
service, so in controller.xml we must add a new handler element immediately
under the other elements:


	<handler name="service" type="request"
			class="org.ofbiz.webapp.event.ServiceEventHandler"/>
		<handler name="service-multi" type="request"
			class="org.ofbiz.webapp.event.ServiceMultiEventHandler"/>

We will cover service-multiservices later. Finally add a new view map:


	<view-map name="TestFirstService" type="screen"
		page="component://learning/widget/learning/
			LearningScreens.xml#TestFirstService"/>

Fire to webapp learning an http OFBiz request TestFirstService, and see that we
have successfully invoked our first service:

Service Parameters

Just like Java methods, OFBiz services can have input and output parameters and
just like Java methods, the parameter types must be declared.

Input Parameters (IN)

Our first service is defined with two parameters:


	<attribute name="firstName" type="String" mode="IN" optional="true"/>
	<attribute name="lastName" type="String" mode="IN" optional="true"/>

Any parameters sent to the service by the end-user as form parameters, but not in
the services list of declared input parameters, will be dropped. Other parameters
are converted to a Map by the framework and passed into our static method as the
second parameter.

Add a new method handleInputParamaters to our LearningServices class.


	public static Map handleParameters(DispatchContext dctx, Map
	context){
		String firstName = (String)context.get("firstName");
		String lastName = (String)context.get("lastName");
		String planetId= (String)context.get("planetId");
		String message = "firstName: " + firstName + "
"; message = message + "lastName: " + lastName + "
"; message = message + "planetId: " + planetId; Map resultMap = ServiceUtil.returnSuccess(message); return resultMap; }

We can now make our service definition invoke this method instead of the
learningFirstService method by opening our services.xml file and replacing:


	<service name="learningFirstService" engine="java"
			location="org.ofbiz.learning.learning.LearningServices"
			invoke="learningFirstService">

with:


	<service name="learningFirstService" engine="java"
			location="org.ofbiz.learning.learning.LearningServices"
			invoke="handleParameters">

Once again shutdown, recompile, and restart OFBiz.

Enter for fields First Name, Last Name, and Planet Id values Some, Name, and
Earth, respectively. Submit and notice that only the first two parameters went
through to the service. Parameter planetId was dropped silently as it was not
declared in the service definition.

Modify the service learningFirstService in the file ${component:learning}\
servicedef\services.xml, and add below the second parameter a third one
like this:


	<attribute name="planetId" type="String" mode="IN" optional="true"/>

Restart OFBiz and submit the same values for the three form fields, and see all three
parameters go through to the service.

Output Parameters (OUT)

Just like Java methods have return values (although Java methods can have only one
typed return value), services can be declared with output parameters. When invoked
as events from the controller, parameters will be silently dropped if they are not
declared in our service’s definition. Add this to our service definition:


	<attribute name="fullName" type="String" mode="OUT" optional="true"/>

And in the method handleParameters in org.ofbiz.learning.learning.
LearningServices replace:


	Map resultMap = ServiceUtil.returnSuccess(message);
	return resultMap;

with


	Map resultMap = ServiceUtil.returnSuccess(message);
	resultMap.put("fullName", firstName + " " + lastName);
	return resultMap;

We have now added the fullName parameter to the resultMap. To see this in action
we need to create a new screen widget in LearningScreens.xml:


	<screen name="TestFirstServiceOutput">
		<section>
			<actions><set field="formTarget" value="TestFirstServiceOutput"/></actions>
			<widgets>
				<include-screen name="TestFirstService"/>
			</widgets>
		</section>
	</screen>

Add the request-map to the controller.xml file:


	<request-map uri="TestFirstServiceOutput">
		<event type="service" invoke="learningFirstService"/>
		<response name="success" type="view" value="TestFirstServiceOutput"/>
	</request-map>

and finally the view-map:


	<view-map name="TestFirstServiceOutput" type="screen"
		page="component://learning/widget/learning/
		LearningScreens.xml#TestFirstServiceOutput"/>

Stop OFBiz, rebuild our Learning Component and restart, fire an OFBiz http request
TestFirstServiceOutput to webapp learning. Submit your first and last names
and planet and notice that now the fullName parameter has been populated.

Two Way Parameters (INOUT)

A service may change the value of an input parameter and we may need a calling
service to be aware of this change. To save us declaring the same parameter twice,
with a mode for IN and a mode for OUT, we may use the mode INOUT.


	<attribute name="fullName" type="String" mode="INOUT" optional="true"/>

Special Unchecked Parameters

There are a few special cases where IN/OUT parameters can exist even though the
service definition does not declare them. They are:

  • responseMessage
  • errorMessage
  • errorMessageList
  • successMessage
  • successMessageList
  • userLogin
  • locale

The parameters responseMessage, errorMessage, errorMessageList,
successMessage and successMessageList are necessary placeholders for feedback
messages. They must be allowed through all validation checks.

The parameter userLogin is often required for authentication and permissions checks.

The parameter locale is needed just about everywhere in OFBiz. For localespecifi
city in certain operations like retrieving template feedback messages, or like
formatting numbers and currency figures.

Optional and Compulsory Parameters

The Service Engine checks the validity of the input and the output to ensure that
what is coming into the service and is leaving adheres to the service definition. If
the optional attribute is set to false and an expected parameter is missing, then
the validation will fail and the service will return an error. This transaction will
now be marked for rollback, meaning any changes to the database made during this
transaction will never be committed. This could include any changes made to the
database by calling services. For example:


	<attribute name="fullName" type="String" mode="INOUT"
			optional="false"/>

Here the parameter fullName must be passed into the service and the service must
also add this parameter to the resultMap and pass it out or validation will fail and
an error will be thrown.

Try changing all of the optional fl ags on our newly created service to false. After a
restart we should see:

The DispatchContext

We have already seen how parameters are passed into our Java method as a Map.
Just as the userLogin object of type GenericValue and the locale object of type
Locale were added as attributes to the request for the Java events, both are now
automatically added to this context map when the service is invoked in this way.

The first parameter, the DispatchContext, contains the remaining tools we need to
access the database, or to invoke other services.

From our Java code we can get access to the following handy objects like this:


	GenericValue userLogin = (GenericValue)context.get("userLogin");
	Locale locale = (Locale)context.get("locale");
	GenericDelegator delegator = dctx.getDelegator();
	LocalDispatcher dispatcher = dctx.getDelegator();
	Security security = dctx.getSecurity();

For a full list of objects that are available from the DispatchContext, take a look
through the code in org.ofbiz.service.DispatchContext.

The service engine is in no way reliant on there being HTTPServletRequest or
HTTPServletResponse objects available. Because of this we are able to invoke
services outside of the web environment and they can be invoked remotely or
scheduled to run “offline”.

email

«»

Comments

comments