2. Working with OFBiz – Apache OFBiz Development

Chapter 2. Working with OFBiz

Having taken our first quick tour of OFBiz, let us now take a stab at making small tweaks to OFBiz. This section will give us a brief tour of the behind-the-scenes components in OFBiz.

Specifically, we will be looking at:

  • Quick yet powerful customizations, to give us a taste of the power we have with OFBiz.

  • Saving and resetting OFBiz data states quickly, to facilitate rapid software development and testing.

  • The general structure (physical files and folders) of OFBiz, so we know how to find our way around in OFBiz.

Bear in mind that the customizations described here contain many new terms and concepts. These terms and concepts may not necessarily be explained in detail in this chapter, but later in the book. For now, we will just follow the steps and go along for the ride.

Adding Our First Field

In Chapter 1, we entered some fictitious personal details as an anonymous shopper. OFBiz keeps track of every shopper, even anonymous ones. Let's revisit the "order" webapp to have a look at the anonymous shopper entity that OFBiz created when we bought something via the ecommerce site. Fire an https request, control/orderview?orderId=WS10000, to webapp ordermgr.

We're looking at the details of the order we created and processed in Chapter 1. In the Contact Information screenlet, we see the familiar details of the anonymous shopper who created the order.

Looking at the name of the anonymous shopper (OFBiz Researcher), we see a system ID (identification number, 10000 in this case) assigned to the shopper. This ID uniquely identifies the shopper in the OFBiz database. Click on the ID to view the detailed profile screen for the shopper. This will bring us to the webapp partymgr. We'll be asked to log in again because we just clicked on an external link (external to webapp ordermgr).

In the Contact Information screenlet here, update the postal address by clicking the Update button beside the address.

We'll be brought to the Edit Contact Information screen. We will be attempting to change this screen, and the data structure behind it.

Changing the Data

We now change the database entity PostalAddress, which is the data structure behind the Edit Contact Information screen we want to customize. For now, it is enough to know that the term entity refers to a database table.

Editing the Entity Definition

An "Entity Definition" is an XML element <entity> that defines the structure of a data entity (think data structure) as well as its relationships (if any) to other data entities.

In the folder ${OFBizInstallFolder}\applications\party\entitydef, edit the file entitymodel.xml. Go to line 962 where we see:

<field name="countryGeoId" type="id"></field>

By default Eclipse doesn't show line numbers. To enable this go to: Window | Preferences... | General | Editors | Text Editors and select Show Line Numbers. Readers can also use Ctrl+L to go to a specific line number.

The entity definition for PostalAddress should be between lines 949 and 996. The above line should come just beneath the following start tag at line 949:

<entity entity-name="PostalAddress" package-name="org.ofbiz.party.contact" title="Postal Address Entity">

and above the end tag at line 996:

</entity>

Under line 962, insert this:

<field name="planet" type="name"></field>

We have now added a new field named planet to the data entity PostalAddress.

Updating the Database

So far, only the entity definition for data entity PostalAddress has changed, but the data structure in the actual database hasn't been updated yet. To update the database, simply restart OFBiz.

Restarting OFBiz works because of two configuration settings in the file ${OFBizInstallFolder}\framework\entity\config file entityengine.xml. The settings are on line 140 to 141:

check-on-start="true" add-missing-on-start="true"

When OFBiz has completely restarted, we should confirm that the database structure was updated successfully. Open the file ${OFBizInstallFolder}\runtime\logs and console.log and look for the following lines:

DatabaseUtil.java:318:WARN ] Entity [PostalAddress] has 18 fields but table [OFBIZ.POSTAL_ADDRESS] has 17 columns. DatabaseUtil.java:330:WARN ] Field [planet] of entity [PostalAddress] is missing its corresponding column [PLANET] DatabaseUtil.java:1711:INFO ] [addColumn] sql=ALTER TABLE OFBIZ.POSTAL_ADDRESS ADD PLANET VARCHAR(100) DatabaseUtil.java:343:INFO ] Added column [PLANET] to table [OFBIZ.POSTAL_ADDRESS]

The following changes can be done while OFBiz is running. Leave OFBiz running from here on.

Changing the Looks

Now that we have changed the data structure, we should change the user-interface to match.

Editing the User-Interface

In the folder ${OFBizInstallFolder}\applications\party\webapp\partymgr \party, edit editcontactmech.ftl. Go to line 32 and remove the extra double-quote just before the> in [<select name="preContactMechTypeId"">]. We have just fixed our first bug, though this little error doesn't manifest itself since it is tolerated and accommodated by an internet browser such as Firefox.

Insert right above line 185:

<#elseif "TELECOM_NUMBER" = mechMap.contactMechTypeId?if_exists>

this:

<tr> <td class="label">Planet</td> <td> <input type="text" size="30" maxlength="100" name="planet" value="${(mechMap.postalAddress.planet)!"}"> </td> </tr>

Checking Our Changes

Start up OFBiz again. Fire an https request to webapp partymgr, which should bring us to the Find Party screen. Enter Last Name and First Name, Researcher and OFBiz respectively, and click on Lookup Party. Our anonymous shopper should turn up. Click on the Party ID link (most likely 10000) to bring up the party details.

In the Contact Information screenlet, edit the postal address by clicking the Update button beside the address. We will be brought to the Edit Contact Information screen for the anonymous shopper's postal address. Note the new user-interface field we added.

We have just enhanced OFBiz to serve in an interplanetary environment. Test out the new field by entering say Jupiter, and clicking Save.

Changing the Flow

If we imagine that the database or persistence layer of OFBiz is like the engine of a car, the user-interface layer would be the steering wheel (or gas pedal or any other interfaces with the human user), and the flow layer would be the wiring between the two. Working the flow allows us to determine whether the gas pedal activates the engines or the windshield wipers.

Leave OFBiz running for this exercise. Do not shut it down.

Let us now imagine that all postal packages on Mars have problems of being too light to withstand the strong winds there. We will need to issue an advisory to anyone dealing with Martian addresses.

Rewiring the "Save" (Update Postal Address) Button

We will rewire the Save button to point to a new screen upon a successful update of the postal address.

In the folder ${OFBizInstallFolder}\applications\party\webapp\partymgr\WEB-INF, edit the file controller.xml. Replace line 131:

<response name="success" type="view" value="editcontactmech"/>

with:

<response name="success" type="view" value="PostalAddressAdvisory"/>

We've just rewired the Save button to go to a view map called PostalAddressAdvisory. A view map is like a view or screen that is presented to the end-user. View maps are covered in detail in Chapter 8.

We need to extend the original controller.xml file. We create a new file called extended.xml right beside controller.xml. The idea is to minimize changes to controller.xml and cleanly separate new additions to an organized consolidated area (extended.xml in this case). We now create a view map called PostalAddressAdvisory. Enter this into extended.xml:

<?xml version="1.0" encoding="UTF-8" ?> <site-conf xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/ site-conf.xsd"> <view-map name="PostalAddressAdvisory" type="screen" page="component://party/widget/partymgr/ OurPartyScreens.xml#PostalAddressAdvisory"/> </site-conf>

To include extended.xml in controller.xml, we use the <include> element in controller.xml. Right above line 23:

<description>Party Manager Module Site Configuration File</description>

insert this:

<include location="component://party/webapp/partymgr/ WEB-INF/extended.xml"/>

Creating the New Widget Screen

Having created the view map that points to a file OurPartyScreens.xml, and specifically a screen widget (explained in Chapter 3) called PostalAddressAdvisory, we now have to create that non-existent screen widget.

In this folder ${OFBizInstallFolder}\applications\party\widget\partymgr, create a file OurPartyScreens.xml. Enter into OurPartyScreens.xml this:

<?xml version="1.0" encoding="UTF-8"?> <screens xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/ widget-screen.xsd"> <screen name="PostalAddressAdvisory"> <section>
<actions> <set field="titleProperty" value="PageTitleEditContactMech"/> <set field="headerItem" value="find"/>
<set field="tabButtonItem" value="editcontactmech"/> <set field="labelTitleProperty" value="PageTitleEditContactMech"/> <script location="component://party/webapp/partymgr/ WEB-INF/actions/party/editcontactmech.bsh"/> </actions> <widgets> <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}"> <decorator-section name="body"> <section> <widgets> <platform-specific><html> <html-template location="component://party/webapp/ partymgr/party/ postaladdressadvisory.ftl"/> </html></platform-specific>
</widgets> </section> </decorator-section>
</decorator-screen> </widgets> </section> </screen> </screens>

Note how the <html-template> element above points to a file postaladdressadvisory.ftl. We'll need to create that file next.

Creating the FTL File

In the directory ${OFBizInstallFolder}\applications\party\webapp\partymgr\party, create a new file named postaladdressadvisory.ftl. In this file, enter the following lines:

<h2><u>Bogus Postal Address Advisory</u></h2>
<br/> v<div style="width:20%"> Please be advised that Martian gravity is just one third of Earth's, and winds can be as strong as 400km/h. All postal packages to Mars must have a mass of at least 20kg to facilitate safe handling. </div>
<br/>
<div style="width:20%"> Please be advised also that this advisory is completely bogus, and that you
should ignore it completely. </div>
<br/>
<a href="<@ofbizUrl>editcontactmech?partyId=$ {partyId}&contactMechId= ${contactMechId}</@ofbizUrl>">Go Back To Editing Postal Address</a>

Checking Our New Wiring

Go back to the Edit Contact Information screen described earlier in this chapter. We can do that via the Find Party screen in webapp partymgr, as described in the section called Checking Our Changes.

Enter for field planet a value, say Venus, or any other value at all. Note how we will now be brought to the advisory screen we just created. Click on Go Back To Editing Postal Address to go back to the Edit Contact Information screen.

We have successfully changed the flow of things by splicing in an advisory screen.

More to the Flow

Our advisory is regarding Martian postal addresses. What if the postal address we're updating is not Martian? We need to make our wiring smarter. We want to add some preprocessing before showing the advisory.

Creating the Wiring for Preprocessing

In the folder ${OFBizInstallFolder}\applications\party\webapp\partymgr\WEB-INF, edit extended.xml. Insert above line 4:

<view-map name="PostalAddressAdvisory" type="screen" page="component://party/widget/partymgr/ OurPartyScreens.xml#PostalAddressAdvisory"/>

this:

<handler name="bsf" type="request" class="org.ofbiz.webapp.event.BsfEventHandler"/>
<request-map uri="PostalAddressAdvisory"> <security https="true" auth="true"/> <event type="bsf" invoke="org/ofbiz/party/party/postalAddressAdvisory.bsh"/> <response name="isMars" type="view" value="PostalAddressAdvisory"/>
<response name="notMars" type="view" value="editcontactmech"/> </request-map>

Note the <event> element's attribute type has a value of bsf. It stands for BeanShell Framework. That means we are coding with the BeanShell scripting language in the file postalAddressAdvisory.bsh. Although quick to prototype, and ideal for the sake of a quick example, BeanShell is slower to execute than compiled java, is very difficult to debug, and at the time of writing is in the process of being replaced in OFBiz by Groovy.

Next, we point the Save button (on the Edit Contact Information screen) to this new wiring for preprocessing. Replace line 131 in controller.xml:

<response name="success" type="view" value="PostalAddressAdvisory"/>

with:

<response name="success" type="request" value="PostalAddressAdvisory"/>

That line no longer points to a view map but to the request map we had just created in extended.xml. Request maps are discussed in detail in Chapter 8.

Creating the Preprocessing

Note how the element <event> in our request map above points to a file postalAddressAdvisory.bsh. That is the file that we must create, the file that contains the pre-processing we need.

In folder ${OFBizInstallFolder}\applications\party\script\org\ofbiz\party\party, create a new file postalAddressAdvisory.bsh and enter into it the following:

import org.ofbiz.party.contact.*;
String partyId = request.getParameter("partyId");
Map mechMap = new HashMap();
ContactMechWorker.getContactMechAndRelated(request, partyId, mechMap);
Map postalAddress = (Map)mechMap.get("postalAddress");
if (postalAddress == null) return "notMars";
String planet = (String)postalAddress.get("planet");
if (planet == null || !planet.equalsIgnoreCase("Mars")) return "notMars";
return "isMars";

Testing Our Smarter Wiring

Go to the Edit Contact Information screen for our anonymous shopper. Enter any value for the field planet, say Pluto. Notice how the flow goes straight back to the Edit Contact Information screen, skipping our advisory.

Now enter a value of Mars for the field planet. Our advisory now shows up.

If OFBiz were a car, it's like we had rewired our gear box to activate a warning whenever we shift into reverse-gear. A warning that says: "Make Way! Backing Up!". Moving the gear stick still shifts the gears, as usual. We had just spliced in an additional workflow, without interrupting the original processes.

And that's our first taste of tweaking this immense beast that is OFBiz.

Some Changes Possible with Engines Running

From the previous sections, we noticed that only the data entity changes require an engine shutdown and a subsequent restart. Changing the looks and the flow of OFBiz can be done on-the-fly.

This is one of the many key advantages that OFBiz offers—the ability to develop the software rapidly. In contrast, anyone familiar with Java (the programming language) will know that its clean object-oriented structure and comprehensive libraries are offset by its traditional "change, re-compile, test" cycle that is common to many programming languages (such as C/C++). Between changing the software code and re-compiling, there is usually a need to shut down the software. Shutting down and then restarting large server-based software can take time, possibly several minutes, much longer than the few seconds of downtime that programmers hope for in-between the tens of thousands of changes they make to a typical software program.

OFBiz provides many development methods that don't require a re-compile for every change made. In general, all user-interface changes in OFBiz do not require OFBiz to be shutdown prior to those changes. As we had seen, the user-interfaces can be changed and tested without a re-compile. Changing the flow of OFBiz also doesn't require the engines to be first stopped.

A few types of changes do require an OFBiz shutdown. Changes to the data entity are one. Changes to OFBiz configuration files are another, though effort is underway to remove that constraint. Changes to Java code will definitely require OFBiz to be shutdown prior to a re-compile.

For good measure, let us now make another change to the user-interface while OFBiz is still running. The field Planet should come right after Zip/Postal Code, and before Country. A single country or nation could span several planets.

In the folder ${OFBizInstallFolder}\applications\party\webapp\partymgr\party, edit editcontactmech.ftl. Cut out the chunk from line 185 to 190 that is the HTML element <tr>:

<tr>
<td class="label">Planet</td> <td>
<input type="text" size="30" maxlength="100" name="planet" value="${(mechMap.postalAddress.planet)!""}">
</td>
</tr>

and paste it immediately above line 166:

<tr>
<td class="label">${uiLabelMap.CommonCountry}</td>
</tr>

Go to the Edit Contact Information screen for our anonymous shopper to see this change immediately take effect.

Resetting Our Play Area Quickly

Before we move on to learn the OFBiz framework proper, we need to get some basic kung fu that will tremendously aid our future exploration of OFBiz. This section describes some common short-cuts or "rapid development techniques" that will convert us from mere tinkerers into serious scientists.

Skipping Some Pre-Ignition Processes

In Chapter 1 we learned how to backup the Derby data files. In this section, we will look at how we can use that backup.

Imagine now that we want to show Mr. Evaluator, a noted ERP systems evaluator, our OFBiz. We want it to be pristine and clean, without the nonsensical test data we had entered thus far. We will first need to destroy the existing data. We delete the existing Derby data files. The Derby data files are in ${OFBizInstallFolder}\runtime\data\derby, contained in a folder ofbiz there. Delete the folder ofbiz there.

Restoring Derby Data Files

We need to unzip our backup of the Derby data files which we created in Chapter 1. You did backup the Derby data files then, right? If you didn't, you will have to go through the long data-loading process again, possibly 15 minutes or more. See Chapter 1 for that process again, and then backup the Derby data files right after.

Unzip our backup of the Derby data files into the folder ${OFBizInstallFolder}\runtime\data\derby files. That's it. We now have a pristine, clean set of data for OFBiz, and Mr. Evaluator won't have to see the weird test data we keyed in.

Removing the Web Server (Catalina) Work Files

We now need to remove the web server work files. Go to folder ${OFBizInstallFolder}\runtime\catalina and delete the folder work.

Work files contain session information from previous visitors as the database references to these visits no longer exist in the database, OFBiz will complain.

Updating the Database with Our Data Entity Changes

In the section called Changing the Data, we changed the data entity PostalAddress. We subsequently changed some user-interfaces to match. Since we now only have the stock plain-vanilla OFBiz database, we will need to update the database again. This process is exactly the same as the section called Updating the Database. Restart OFBiz to update the database.

Showing Off Our Spanking New OFBiz Installation

The restoration of the Derby data files and the deletion of web server work files should take no more than a few minutes. Our installation of OFBiz is now pristine again, untouched by any prior usage.

It's time to show off our spanking new OFBiz installation to Mr. Evaluator. We buy four round gizmos as an anonymous shopper, key in details for the anonymous shopper, and then confirm and create the order. We log in to webapp ordermgr and pull up the order's details, and approve the order. We are about to receive some offline payment. But wait! Mr. Evaluator has something to say!

Tripping Up Our Plan

At this point, Mr. Evaluator suddenly asks a question that would derail the flow of our presentation plan. Or would it? "What if I ship the order before receiving payment?" Mr. Evaluator asks.

It would seem we now have only one option; to tell Mr. Evaluator to let us finish our presentation first and then go through other "scenarios" from square one. Fortunately, we can start from square one easily, after a few minutes of restoring the original Derby data files and clearing the web server work files.

Or we could do something more impressive here, and say the following:

  • "computer, freeze program, and store save-point"

  • "computer, run scenario B that Mr. Evaluator just described"

  • "computer, run scenario A from last save point"

Well, OFBiz still doesn't take voice commands. What we're about to do is exactly as impressive as the above, though without the voice commands.

Storing a Save-Point to Dramatically Ease Testing

Before we can store a save-point, we need to shut down OFBiz first. Do so now!

Archiving Derby Data Files and Web Server Work Files

The process to backup the Derby data files was already described, but the process to backup the web server work files is yet to be covered. We'll cover both processes here so we can have it all in one place.

Backing Up the Derby Data Files

The Derby data files are in ${OFBizInstallFolder}\runtime\data\derby, contained in a folder named ofbiz.

Go to ${OFBizInstallFolder}\runtime\data\derby and zip up the Derby data files into a single archive say ofbiz_4_0_r589272_SavepointA.zip. Keep the archive safe somewhere.

Backing Up the Web Server Work Files

The web server work files contain web sessions and session IDs. OFBiz stores some web visit details in its database, and these details tie in with the web server work files. Hence, the web server work files need to be correctly matched with the Derby data files. That means we save both and archive them together when creating a save-point.

The web server work files are in the folder ${OFBizInstallFolder}\runtime\catalina, contained in the folder work. Backup the folder work and keep the archive safe somewhere, together with the corresponding Derby data files backup we created for this save-point, say in an archive ofbiz_4_0_r589272_SavepointA_web.zip.

Computer, Run Scenario B that Mr. Evaluator just Described

We start up OFBiz, and go back to webapp ordermgr to view our order. This time, instead of receiving payment first, we do a quick ship first. We then receive an offline payment for the whole order ($65.30).

Following the tour outlined previously, we click on the automatically generated invoice for the order. And we see a difference! The invoice's "Open" amount is still $65.30, while "Applied Payments" is $0.

Mr. Evaluator lets out a gasp of surprise, and asks why received payments are not automatically applied to automatically generated invoices. We make a quick save and say that OFBiz is really a huge beast that has many work-in-progress areas. To solidify the save, we add that "this auto-application of received payments is already implemented, though it is hooked up somewhere else in OFBiz".

We now move quickly to show Mr. Evaluator that OFBiz does automatically apply received payments.

Restoring a Save-Point

Before we can restore a save-point, we need to shut down OFBiz first. Do so now. Since we are discarding the current state of OFBiz, we must first remove the existing Derby data files and web server work files. Do so now!

Restoring the Derby Data Files

Unzip into ${OFBizInstallFolder}\runtime\data\derby the Derby data files archive ofbiz_4_0_r589272_SavepointA.zip, which we had created when we stored the save-point we are now restoring. We should have restored the folder ofbiz.

Restoring the Web Server Work Files

Unzip into ${OFBizInstallFolder}\runtime\catalina the web server work files archive ofbiz_4_0_r589272_SavepointA_web.zip. We should have restored the folder work.

Computer, Run Scenario A from Last Save-Point

We are now back to looking at an order that has neither been shipped nor received payment. We now receive payment first, and then perform a quick ship. We look at the automatically generated invoice, and show Mr. Evaluator that the received payment was automatically applied to the invoice.

We wait for applause. None comes. Mr. Evaluator then asks, "how long will it take you to put this logic, this auto-application of received payment, into scenario B?". Don't sweat! That's what this book is for. We'll learn how to perform such tweaks and customizations and enhancements. So let's get learning!

The Structure of OFBiz in General

Let's take a quick overview of the general structure of OFBiz, so we know how to move around, and know where to find what. Note that the following describes conventions in OFBiz. We can certainly break those conventions and misplace files, but that will make things more difficult to maintain and understand. For example, a postal address is always structured to be ordered in descending precision of geography, with the more pinpoint address (house number, street name) at the top and the more general locale (state, province, country) at the bottom. Postal services will find it easier to deliver our mail if we follow that convention. Similarly, an organized OFBiz structure makes OFBiz easier to work with.

Components in OFBiz

OFBiz files are organized according to components, with each component being contained in a single folder.

The core application OFBiz components can be found in ${OFBizInstallFolder}\applications, where we have already seen the "ecommerce" component (folder ecommerce) and the "party" component (folder party). To qualify as a core component, a component must be integrated into OFBiz and contain functions that serve a wide denomination of users. Such components are so commonly used that they have become an integral part of OFBiz.

The framework OFBiz components can be found in ${OFBizInstallFolder}\framework. These components are the foundations of OFBiz. They are responsible for database access, caching, rendering the screens, managing transactions, and many more low-level tasks used by the application components. Very often, it is not necessary to understand the code found inside these components—only how to use the features is important.

Special purpose components are those that don't quite qualify as core OFBiz components. They serve only a small fraction of OFBiz users. OFBiz can (or should) function without these components, since they are (or should be) optional. Such components reside in ${OFBizInstallFolder}\specialpurpose.

Each OFBiz component is pretty much self-contained (other than for its relationships with other components). Each OFBiz component has data entity definitions, view definitions, flow definitions (concepts touched on tangentially in the section called "Doing Our First Customization"), the whole works. Yet, OFBiz components often need to work with each other.

The typical OFBiz component structure may consist of configuration files, seed data definition, entity definition, flow logic, service definition (Service Engine discussed later in book), Java code, webapps, and view definitions. All of these will be discussed in greater detail in later chapters.

Referencing Components in OFBiz

In OFBiz, components are referenced via special URIs (Universal Request Identifier) that we call "component URIs". A component URI is of the format component: //<component-name>/relative-path, where <component-name> is the name of any component in OFBiz.

The name of a component is found in the definition of the component, that definition residing in the file ofbiz-component.xml, a file that we will explore in the section called Creating Our Own OFBiz Component. Each OFBiz component has its own ofbiz-component.xml.

To see a list of all OFBiz components and their names, go to the View Components screen of webapp webtools at https request ViewComponents. The second column of that list will show the root folder of each component.

In this book (not in OFBiz code examples), we will from now on refer to these components root folders with this format: ${component:<component-name>}. For example, the folder ${OFBizInstallFolder}\applications\party will be referred to as ${component:party}.

Creating Our Own OFBiz Component

Having learned about an OFBiz component's file structure convention, let us now create our very first OFBiz component. We will call this component learning and will use it to perform our messy mad-scientist work and experimentation, far away from the official and sensitive parts of OFBiz.

Creating the Component

Custom OFBiz components are usually created in the folder ${OFBizInstallFolder}\hot-deploy. So long as the structure criteria is met, components placed into hot-deploy will be automatically loaded after those in the framework and applications. There is no need to specify them in component-load.xml files, as has to be done in the framework and applications. So, in hot-deploy, create a new folder learning. This can be done very quickly from our Eclipse project's Navigator view by right-clicking on the hot-deploy folder, selecting New | Folder, and entering its name.

In the folder learning we just created, create a new file named ofbiz-component.xml. Again, this can be done in Eclipse by right-clicking on the learning folder and selecting New | File. In this file, enter the following:

<?xml version="1.0" encoding="UTF-8"?> <ofbiz-component name="learning" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/ ofbiz-component.xsd"> </ofbiz-component>

Note that the OFBiz component name we have chosen is learning (see attribute name of element <ofbiz-component>). This name must be unique across the entire OFBiz implementation. To see a list of components and their names and other details, fire an https request ViewComponents to webapp webtools.

Using Our Component

Here, we take the opportunity to clean up the mess we created in the "party" component. Along the way, we will learn to use the OFBiz component we just created. We will also learn some neat OFBiz mechanisms for "clean extensions". "Clean extensions" is code that extends original OFBiz code while minimizing changes to the original OFBiz code.

Cleaning Up Our Mess in the "party" Component

There are a total of 4 files we need to move out of the "party" component. These files are the ones we created and which don't belong to the "party" component:

  • extended.xml

    (In folder ${component:party}\webapp\partymgr\WEB-INF)

  • OurPartyScreens.xml

    (In folder ${component:party}\widget\partymgr)

  • PostalAddressAdvisory.ftl

    (In folder ${component:party}\webapp\partymgr\party)

  • PostalAddressAdvisory.bsh

    (In folder ${component:party}\script\org\ofbiz\party\party)

We will move those files into folder ${component:learning}.

In ${component:learning}, create three nested folders webapp\partymgr\WEB-INF. Move the file extended.xml into the deepest folder. In the folder ${component:party}\webapp\partymgr\WEB-INF, edit the file controller.xml and change line 23:

<include location="component://party/webapp/partymgr/ WEB-INF/extended.xml"/>

to:

<include location="component://learning/webapp/partymgr/ WEB-INF/extended.xml"/>

Note how the new component URI now points to the component learning, instead of the component party.

In ${component:learning}, create two nested folders widget\partymgr. Move the file OurPartyScreens.xml into the deepest folder. Edit extended.xml and change line 13:

<view-map name="PostalAddressAdvisory" type="screen" page="component://party/widget/partymgr/ OurPartyScreens.xml#PostalAddressAdvisory"/>

to:

<view-map name="PostalAddressAdvisory" type="screen" page="component://learning/widget/partymgr/ OurPartyScreens.xml#PostalAddressAdvisory"/>

In the folder ${component:learning}\webapp\partymgr, create a folder party. Move the file PostalAddressAdvisory.ftl into that folder. Edit OurPartyScreens.xml and change line 20:

<html-template location="component://party/webapp/partymgr/party/ PostalAddressAdvisory.ftl"/>

to:

<html-template location="component://learning/webapp/partymgr/party/ PostalAddressAdvisory.ftl"/>

Converting the BeanShell to a Java Event

Finally we come to the BeanShell file. Rather than simply copying this over, now would be an ideal time to take a look at the far more common approach to dealing with events in OFBiz: Java.

Edit extended.xml and replace line 8:

<event type="bsf" invoke="org/ofbiz/party/party/postalAddressAdvisory.bsh"/>

with:

<event type="java" path=" org.ofbiz.learning.learning.learning" invoke="postalAddressAdvisory"/>

We also need to tell the request-map that the type of event has changed, so in the same file replace:

<event type="bsf" invoke="org/ofbiz/party/party/postalAddressAdvisory.bsh"/> <response name="isMars" type="view" value="PostalAddressAdvisory"/>

with:

<event type="java" path="org.ofbiz.learning.learning.LearningEvents" invoke="postalAddressAdvisory.bsh/>

In the folder ${component:learning}, create five nested folders src\org\ofbiz\learning\learning.

We are going to insert a Java class into this folder but first we have to tell Eclipse that this folder is going to contain Java code. To do this right-click on the blue, project root, ofbiz4.0 folder from the Navigator box and select Properties, then Java Build Path. Select the Source tab and click Add Folder.... In fact we want Eclipse to know that each file in the whole src folder is going to be Java class, so navigate to this folder, highlight the checkbox and click OK to add this folder as a source folder and OK again to leave the Project Properties dialog box and return to our project. Now, if we right-click on the party folder and choose to add a new class, the correct package information will be added automatically to the class. Let's call our class LearningEvents. Just adding LearningEvents in the Name field and clicking Finish is enough. There is no need to add .java to the end of the name; Eclipse will do this for us.

In the new class, we want to add just one method called postalAddressAdvisory which is invoked from the controller entry we just changed. The final class should look like this:

package org.ofbiz.learning.learning;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.ofbiz.party.contact.ContactMechWorker;
public class LearningEvents {
public static String postalAddressAdvisory(HttpServletRequest request, HttpServletResponse response){
String partyId = request.getParameter("partyId");
Map mechMap = new HashMap();
ContactMechWorker.getContactMechAndRelated(request, partyId, mechMap);
Map postalAddress = (Map)mechMap.get("postalAddress");
if (postalAddress == null) return "notMars";
String planet = (String)postalAddress.get("planet");
if (planet == null || !planet.equalsIgnoreCase("Mars")) return "notMars";
return "isMars";
}
}

The method must be made static as the OFBiz framework calls the method and does not instantiate the class LearningEvents. The framework also takes care of passing in the parameters HttpServletRequest request and HttpServletResponse response.

Unfortunately, adding the learning folder under hot-deploy, does not automatically include it in the project compilation. First of all, a build.xml file must be created under the learning folder at the same level as the ofbiz-component.xml file. A quick trick here is to copy the build.xml file from ${OFBizInstallFolder}\applications\party to ${OFBizInstallFolder}\hot-deploy\learning and replacing everywhere it says Party to Learning—there are four places on lines 21, 29, 30, and 112. We now need to change just one dependency. Since the party component code depends on some code in the content component, and our module depends instead on some code in the party module, we must change line 51 from:

<fileset dir="../content/build/lib" includes="*.jar"/>

to:

<fileset dir="../../applications/party/build/lib" includes="*.jar"/>

Once this has been done, make sure OFBiz is not running and in the Ant window on the right-hand side, double-click the OFBiz Main Build entry to compile and build the whole project. Once compiled, select the learning folder in the Navigator view and hit F5 to refresh the project, you will now see a new folder called build which contains the classes and lib folders. Within the lib folder there should be a file called ofbiz-learning.jar.

In some of the future examples, there may not always be a need to recompile the entire project. By dragging the build.xml file from your learning folder over into the Ant window, you can quickly double-click this while you are working on something in the main window. This can cut the compilation time down from a minute or more to a second or less.

Finally, before we restart, we must add the folder that has just been created to the classpath. To do this, open the rather empty ofbiz-component.xml file and insert:

<classpath type="jar" location="build/lib/*"/>

Rebuild the whole project and restart.

Clean Extension Strategies Employed

A few clean extension strategies were employed in the above.

The most obvious strategy we can see above is inter-component referencing via the component URIs. Being able to reference another component, say referencing the learning component from the party component in our case above, allows us to keep components largely unchanged while extending them with a separate component. In our examples above, the party component was only minimally changed merely to point to the actual extensions coded in the learning component.

Another trick is the "controller include" strategy with the <include> element, which lets us extend a controller.xml file. As with the inter-component referencing strategy described above, this trick lets us keep the controller.xml file clean with a mere pointer to the extensions coded in the file extended.xml.

By employing clean extension strategies from the very beginning we are able to cleanly separate the core code from our own bespoke code. When it comes time to upgrade, this will considerably cut down the time and complexity of this task. It could even mean the difference of being able to upgrade or not.

Checking that Our Move was Successful

Restart OFBiz, go to the webapp party and bring up any postal address for edit. Ensure that the original behavior with the field planet is still retained. If so, our move was successful, and the party component is now free of our new files.

A Bit More Mess Remains

The file ${component:party}\entitydef\entitymodel.xml was changed to add a new field planet.

The file editcontactmech.ftl in the folder ${component:party}\webapp\partymgr\party still contains a chunk of enhancement code to display the new field planet. Clean extension tricks in this area will be described in the next chapter on screen widgets.

Webapps in OFBiz

An OFBiz component by itself cannot be accessed by end-users. It is simply a means of organizing OFBiz into individual parcels focused on dealing with individual aspects of ERP software. Webapps or web applications provide the front-end through which end-users can work with and use OFBiz.

Think of a webapp as an individual "office opened for service". Such a virtual "office" can recognize and respond to several different "requests" (listed as request maps in controller.xml).

Webapps are contained inside OFBiz components. Typically, each OFBiz component has one webapp, but can actually have more. The ecommerce component has two, named ecommerce and e-comclone, because OFBiz developers wanted to showcase how multiple storefronts can be set up based on the single consistent set of functionalities coded in the ecommerce component. The product component also has two, named catalog and facility, because product management ("catalog") and inventory handling ("facility") work off of logic that are closely intertwined.

As mentioned before, OFBiz components are simply a way to organize OFBiz into manageable chunks or logically distinct aspects of ERP software. Technically, it is certainly possible to put all 17 webapps in OFBiz under a single component, though to do so would be quite illogical and would make things very unwieldy.

Webapps are possible in OFBiz because it has an embedded web server called Tomcat.

Creating Our First Webapp

All that makes a webapp is a <webapp> element in an OFBiz component's ofbiz-component.xml flie. Edit ${component:learning}\ofbiz-component.xml and insert into the <ofbiz-component> element a <webapp> element like this:

<webapp name="learning" title="Learning" server="default-server" location="webapp/learning" base-permission="NONE" mount-point="/learning"/>

From this fact, we will know instantly that the folder ${component:learning}\webapp\partymgr is not a webapp; it is not defined in ofbiz-component.xml as a webapp location. Instead the location is pointing to webapp\learning. This path does not yet exist in our project and must be created before we restart.

The title attribute determines the text shown in the appbar, which is the toolbar that lists all webapps in OFBiz. The appbar is right below the OFBiz logo that says The Apache Open for Business Project and is sorted in alphabetical order. If an attribute app-bar-display is present and has a value of false, the webapp will not appear in the appbar.

The location attribute is a path to our webapp folder, relative to the component folder. In this case, the absolute path to our webapp folder is ${component:learning}\webapp\learning, so the relative path to it (after chopping off the ${component:learning} portion) is webapp\learning.

The mount-point attribute determines what URL the end-user will use to access our webapp. In this case, our end-user will access our webapp with a URL like http://localhost:8080/learning. We can just as easily mount it somewhere else fanciful, like at "special-learning", but let's not make it harder for us to type out URLs.

The name attribute doesn't seem to be important, as webapps are never referenced in OFBiz by their names. Still, this may change in the future, and there might be webapp URIs similar to component URIs. For now, we should try to keep the name attribute exactly the same as the mount-point attribute.

The other attributes will be explained in more detail later in the book.

Webapp URIs in this Book

Although webapp URIs do not yet exist in OFBiz, we will use a short hand term to refer to webapp roots in this book, for the sake of brevity and readability. The short-hand will be of this format: ${webapp:<webapp-name>}. Therefore, a pathname say ${component:learning}\webapp\learning will be referred to as ${webapp:learning}. For paths that are mere folders and not webapps, like ${component:learning\webapp\partymgr, they will continue to be referred to via that form.

Webapp names will be derived from the mount-point attribute minus the leading "/", not the name attribute. Mount points need to be unique. It makes no sense to have name different from mount point. Also it makes no sense to mount two webapps with different name attributes onto the same mount point.

Testing Our First Webapp

We want to know if our webapp learning has been set up correctly and is accessible to the end-user.

In the folder ${webapp:learning}, create a new file called index.html and enter into it the following:

This is webapp "learning" responding with this text.<br/> If you're seeing this, you have successfully called on the webapp.<br/> Do not call SETI; this is not a message from "out there".

Restart OFBiz and fire an http request index.html to the webapp learning. You should see that silly message we just entered into the file.

The Model-View-Controller Architectural Pattern

OFBiz uses a tried-and-tested software organizational structure called the Model-View-Controller (MVC). Since this book is organized into parts following the MVC architecture, we will take a quick overview of how OFBiz files correspond to MVC.

The MVC in Plain English

The Model refers to the data structure of the software. Data structures are often used to virtually describe a real-world system, much like how a model railway system with model trains describe the real railway system in the real world. A typical "model" may consist of a "customer" entity and a "sales person" entity in a "many-to-one" relationship that mimics a real-world situation where one sales person serves several customers. As can already be seen here, a "model" is a collection of data entities in a network of relationships with one another.

The View refers to the "outsides" of the software, the component that is handled by the human user (end-user)—the user-interface.

The Controller refers to the wiring between the Model and the View. The Controller part can be huge, and is almost always larger than the Model and the View. The Model and View are quite static. The Model being a fixed collection of data entities in a fixed network of relationships, and the View being a collection of user-interfaces that may be described as static dashboards. In contrast, the Controller is the dynamic behavior defined by nothing less than a hairball of wiring.

This section describes the MVC architecture of OFBiz in the context of an OFBiz component. That is, all file locations described here are relative to the root of an OFBiz component. As mentioned in the previous section, OFBiz components are self-contained save for their working relations with each other. So, each component has its own MVC structure.

The Model in OFBiz

The Model in OFBiz is defined in data definition XML files, such as in the file folder ${component:party}\entitydef entitymodel.xml which we played with earlier in the chapter in Changing the Data.

Such files are always (or should always be) located in folders named entitydef. Each OFBiz component can have one (or zero) of such folders. It follows that each OFBiz defines its own "little world" or "distinct model". The party component, for example, has a model that describes parties or people, such as shoppers and back office staff. And that "distinct model" is cleanly separated from the model in say order component where customer orders are modeled.

Within each "distinct model", there can be a network of relationships between individual data entities. This is seen in the way the entity Party is in some way linked to entity PostalAddress so that people can have postal addresses attached to them.

Between two separate "distinct models", there can be relationships too. For example, the entity Party in the party component can be linked to the entity Order in the order component, such that shoppers can be linked to their orders.

The View in OFBiz

Views or user-interfaces in OFBiz can reside in two different places, depending on which technology is being used to define those views. The technologies themselves will be discussed in more detail later on in the book.

When using the OFBiz widget technology, user-interface definitions reside in folders named widget. Each OFBiz component can have one such folder. Here, it must be noted that OFBiz does have some of these files misplaced in webapp folders. OFBiz is very large and still has many work-in-progress areas. OFBiz may also contain some remnants of former programmer habits and mistakes or old fashioned techniques, or perhaps even spelling mistakes, having been unnoticed in development for six years.

When using HTML or FTL technologies, user-interface definitions should reside in folders named webapp. Note that this excludes the folder WEB-INF inside each webapp folder—that folder contains flow components or business logic.

The Controller in OFBiz

Controller components or "flow" or business logic in OFBiz can reside in three different places, depending on the technology used to define business logic.

When using pure Java, business logic definitions should reside in folders named src.

When using Minilang or BeanShell, business logic definitions usually reside in folders named script, and also in the folder WEB-INF\actions inside webapp.

When using Event-Condition-Action (ECA), business logic is defined in the folder servicedef, in the file secas.xml when using Service ECA; and in the folder entitydef in the file eecas.xml when using Entity ECA.

When using OFBiz Services, business logic definitions span over the folders servicedef and script. In the folder servicedef, files with names starting "services" (for example services.xml, service_view.xml) declare the usage pattern of services. Complementing that, in the folder script or src, we have the actual implementation or definition of those services.

The files controller.xml and web.xml in the folder WEB-INF are also considered controller components in the MVC architecture.

Other Files in an OFBiz Component

There are other files besides those described above, files that don't fit into the MVC architecture. Some are configuration files; some are seed data files. These files will be explained when we need to deal with them later in this book.

Summary

In this chapter, we looked at:

  • Some quick yet powerful tweaks to OFBiz, covering the whole spectrum of MVC.

  • Saving data states by archiving.

  • Derby data files in the folder ${OFBizInstallFolder}\runtime\data\ derby\ofbiz.

  • Catalina work files in the folder ${OFBizInstallFolder}\runtime\ catalina\work.

  • Restoring data states by restoring the above archives.

  • The structure of OFBiz, which is made up of components, with zero or more webapps in each component.

  • Creating a component of our own with an <ofbiz-component> element in the file ofbiz-component.xml.

  • Creating a webapp of our own with a <webapp> element within a <ofbiz-component> element.

  • Component URIs of the form component://<component-name>/ <relative-path>

  • This book's component paths of the form ${component:<component-name>}\<relative-path>.

  • This book's webapp paths of the form ${webapp:<webapp-name>}\<relative-path>.

  • The MVC architecture in brief, and how OFBiz uses it.

  • The folder structure of OFBiz, so we know how to get around in OFBiz.

If the tweaks in this chapter look complex, that's just because OFBiz is so full of possibilities. We next plunge into the OFBiz framework proper, so we can learn to do non-trivial tweaks and more. Before long, we should be able to do a complete and competent customization of OFBiz for just about any business we fancy.

So, let us buckle up and delve into the OFBiz framework, and be "Open For Business" in no time! In the next chapter, we will start with a screen widget which is, a part of the "View" portion of the MVC.