Webcam Chat QuickBooks Advice international calling cards international phone cards
JavaBeat Certifications Certifications Kits Articles Tutorials Tips QNA Book Store Interview Questions SCJP 1.5 SCJP 1.6 SCWCD 5.0 SCBCD 5.0 SCEA SCJA Feeds
Kaspersky Anti-Virus 2011
Submit Your Blog Feedback Request Article Print Email

Apache OFBiz Development : The Service Engine

Author : PacktPub
Topic : webservice books 
Pages :
Hibernate Books | Spring Books | JSF Books | Java Books
Title : Apache OFBiz Development : The Service Engine
Publisher : PacktPub
Topic : webservice
Related : Hibernate, Spring, Struts, ejb
Javabeat : Tips, Java / J2EE Tutorials, Certifications

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".

Recommended Books

Submit Your Blog Feedback Request Article Print Email

Java / J2EE Tutorials

Spring Framework

Hibernate Framework

JSF Framework

Struts Framework

Java Server Pages(JSP)

Servlets

Java / J2EE Design Patterns

SCJP

SCEA


JavaBeat Website (2004-2009), India
javabeat | advertise | about us | useful resources
Copyright (2004 - 2009), JavaBeat