Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Creating Practice Application

 Written by: Pranay Pandey with feedback and contributions from Chirag Manocha, Ravindra Mandre, Rob Schapper
 Special thanks to Mridul Pathak for inspiring me to write this tutorial. 

This document is meant for OFBiz  beginners. It will help to come up with the most basic knowledge of This tutorial is designed for beginners with little or no OFBiz experience. It covers the fundamentals of the OFBiz application development process. Aim behind the creation of this tutorial The goal is to acquaint make a developer conversant with best practices, coding conventions and the , basic control flow.

Note - 1 :- For any additional query and concern you can consult to Example component. You will always find the code in example component to be the most latest code of Ofbiz. Take reference whenever you want to see some sample code for the development of this application.
Every new feature is first added in the Example component for the references.
Note - 2 : Before starting the development of this application you must read the contents form :
            http://docs.ofbiz.org/display/OFBADMIN/OFBiz+Contributors+Best+Practiceshttp://docs.ofbiz.org/display/OFBADMIN/Coding+Conventions and http://docs.ofbiz.org/display/OFBADMIN/Best+Practices+Guide

Note - 3 : Dont copy any file from other component, as the revision number for the file is also copied. Always create new file and if required then copy the contents of the file." Also be conscious about the unused code as well.

Note - 4 : For searching any of the document the best place is at : http://docs.ofbiz.org/display/OFBADMIN/OFBiz+Documentation+Index .   

Create first basic application by name "practice" :

Step - 1 : Create the sub - directory in hot-deploy/ and name it "practice"(hot-deploy/practice). The directory name should match the new components name that we are creating.

Note : Remember all customised development is done at this place only. 

Step - 2 : Create the ofbiz-component.xml file on path hot-deploy/practice and place following content in it (for reference you can check this file in any other component of OFBiz):

<?xml version="1.0"encoding="UTF-8"?>

<ofbiz-component name="practice"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/ofbiz-component.xsd">

   <resource-loader name="main" type="component"/>
   
    <webapp name="practice"
       title="Practice"
       server="default-server"
       location="webapp/practice"
       mount-point="/practice"
       app-bar-display="false"/>

</ofbiz-component>

ofbiz-component.xml explained:

1. The ofbiz-component.xml file is responsible for letting ofbiz know where resources are at as well as allowing you to add to the classpath.

2. Here 'resource-loader name' can be any string here it is "main". The 'type' tells ofbiz that we will be loading a component.

3. In <webapp> tag, we have different attributes and their purpose is as follows:

    a) name :- defines the name of our web application.

    b) title :- This will be the title of our component 

    c) server :- This will let ofbiz know what server to use 

    d) location :- This will be the location that is the default base directory for the server 

    e) mount-point :- This is the URL used to access this resource. in this case it would be localhost:8080/practice 

    f) app-bar-display :- This will let ofbiz know if we want our component to show up in the main application tabs that are part of the common ofbiz decorator.  

Creating the web app:

Step - 1 : Create a "webapp" directory in the practice component (hot-deploy/practice/webapp).
This directory contains all the webapp related files for the component we are creating.

Step - 2 : Create a sub-directory inside the webapp directory by the name of  "practice" which is the name of the webapp which you are going to develop (hot-deploy/practice/webapp/practice). A component can have multiple webapp's attached to it. e.g. In the "marketing" component there are two webapps "marketing" and "sfa".
The webapp we are creating will follow the J2EE webapp standards.

Step - 3 : Create WEB-INF directory in your webapp (hot-deploy/practice/webapp/practice/WEB-INF).
An OFBiz web application requires two configuration files, a controller.xml and a web.xml. The controller.xml tells OFBiz what to do with various requests from visitors: what actions to take and what  pages to render. web.xml tells OFBiz what resources (database and business logic access) are available for this web application and how to handle web-related issues, such as welcome pages, redirects, and error pages.

Step - 4  Create a file named "web.xml"(web.xml follows j2ee webapp specs). Contents of this file can be taken from any of the existing component e.g. example component. The Important values to change are the <display-name>, the localDispatcherName, and the mainDecoratorLocation.
For now put the value: "component://practice/widget/PracticeScreens.xml" in for the location and you will see why in a while.

Step - 5  Create a file named "controller.xml" (used by ofbiz webapp controller)  This file will be small and simple at first but will grow as we add functionality later on. For now insert the following code:

<?xml version="1.0" encoding="UTF-8"?>

<site-conf xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/site-conf.xsd">

       <include location="component://common/webcommon/WEB-INF/common-controller.xml"/>

       <description>Practice Component Site Configuration File</description>
       <owner>Copyright 2001-2008 The Apache Software Foundation</owner>

       <!-- Request Mappings ->

       <request-map uri="main"><security https="false" auth="false"/><response name="success" type="view" value="main"/></request-map>

       <!-- end of request mappings ->

       <!-- View Mappings ->

       <view-map name="main" type="screen" page="component://practice/widget/PracticeScreens.xml#main"/>

       <!-- end of view mappings ->
</site-conf>

Step - 6 : Move up one level and create a new directory named 'error'(hot-deploy/practice/webapp/practice/error).

Step - 6.a :  Create a file error.jsp inside the "error" directory. Contents of this file can be taken from any of the existing component  e.g. example component.
 The location of your error page will be specified in the beginning of your controller.xml file like <errorpage>/error/error.jsp</errorpage> . You will need to make or copy over a /webapp/practice/error/error.jsp page to show an error message to the user.

Step - 7 : Create a sub-directory inside your component directory "practice" named "widget"(hot-deploy/practice/widget).
              This directory will contain your forms and screens which will be created for UI.

Step - 8 : Create a file inside the directory "widget" named "PracticeScreens.xml".
 Contents of this file can be taken from any of the existing component  e.g. example component.

Very first screen will be like :

<?xml version="1.0" encoding="UTF-8"?>

, and all other aspects that a developer needs for OFBiz customization.

This tutorial will help you in building your first "Demo Application" in OFBiz.

Info
titleImportant!

Post your questions to the OFBiz User Mailing List. Details about the mailing lists are available here


Info
titlePre-Requisites
  • For the Trunk version, the ofbiz-framework trunk and ofbiz-plugins trunk the minimum requirement you need to be installed is Java 1.8 SDK or Sun JDK 1.8.
  • Apache OFBiz can be downloaded and run on both Unix-based and Windows-based systems.
  • Set JAVA_HOME environment variable (It is required to run Gradle build tool)
    Execute the following command on terminal: echo $JAVA_HOME (this will show a path(i.e home directory of your Java installation) which means the variable is set and if not please set)
  • System specific requirements are available here.


Info
titleThis tutorial is for trunk release. For release specific tutorials, please refer:

The branch-specific naming convention is taken based on the year and month in which the branch has been created.
For example Release 18.12 - Here 18 represents the Year 2018 and 12 represents to 12th Month(i.e December). The latest branch is the greatest branch and is highly recommended for general-purpose use.


Info
titleSource Code!

Apache OFBiz
Download Apache OFBiz®
OFBiz Source Repository and Access

Tutorial
The source code of the Practice Application demonstrated in this tutorial can be downloaded from [TODO].

Framework Introduction Videos
OFBiz YouTube Channel or Vimeo can be accessed for the same.

Table of Contents

Overview (Introduction to OFBiz)

Open For Business (OFBiz) is a suite of enterprise applications built on a common architecture using common data, logic and process components. The loosely coupled nature of the applications makes these components easy to understand, extend and customize.

The tools and architecture of OFBiz make it easy to efficiently develop and maintain enterprise applications. This makes it possible for us as the creators and maintainers of the project to quickly release new functionality and maintain existing functionality without extensive effort. It also makes it easy to customize and extend existing functionality when you have a specific need.

The architecture alone makes it easier for you to customize the applications to your needs, but many of the best flexibility points in the system would be meaningless and even impossible if the system was not distributed as open-source software. OFBiz is licensed under the Apache License Version 2.0 (ASL2) which grants you the right to customize, extend, modify, repackage, resell, and many other potential uses of the system.

No restrictions are placed on these activities because we feel that they are necessary for the effective use of this type of software. Unlike other open-source licenses, such as the GPL, your changes do not have to be released as open-source. There are obvious benefits to contributing certain improvements, fixes, and additions back to the core project, but some changes will involve proprietary or confidential information that must not be released to the public. For this reason, OFBiz uses the ASL2 which does not require this. The only required thing is to not remove the "copyright, patent, trademark, and attribution notices" you find in files. For more information on open source licenses see the Open Source Initiative (OSI) website at www.opensource.org.

Another benefit of this open-source model is that we receive constant feedback from those who are using the software. We have received countless bug fixes, improvement suggestions, and best-practice business advice from users and potential users of OFBiz. Many of the greatest features in the project were inspired by some comments or suggestions sent to the mailing lists associated with the project. With dozens of organizations using the software and probably hundreds of deployed sites using one piece or another of the project we generally get 20-30 emails each day about the project.

To make sure our functionality is timely and useful we always start by researching public standards and common usage for any component we are working on. This helps us support and use common vocabularies and gives us an instant breadth of options and features that can only be achieved through standards processes and other group efforts. It also opens doors in the future for flexible communication with other systems that are built around the same standards, both inside your organization and in the partner or other organizations.

The applications and application components that come with the system provide you with a broad and flexible basis that can be used as-is with the best-practices-based designs or customized to your own special needs. The applications facilitate the management of everything from parties and products to accounting, customer service, and internal resource and asset management.

Reference: https://ofbiz.apache.org/

Setting up and Running OFBiz

Download Apache OFBiz Framework

If you haven't already checkout Apache OFBiz Framework on your machine, let's do it. Anyone can checkout or browse the source code in the OFBiz public GIT repository. If you don't have Git, to install it you can go here for instructions.

To clone the source code, simply use the following command (if you are using a GUI client, configure it appropriately):

Since the trunk was split into ofbiz-framework and ofbiz-plugins, the specialpurpose and hot-deploy directories have disappeared.
New components must be put in a plugins directory which works as was the hot-deploy directory.

For OFBiz existing components, check them out using the Gradle tasks or the GIT command

1.Checkout via Gradle tasks

To get all components use pullAllPluginsSource(This command should be run from ofbiz-framework home). Beware this deletes a previously existing plugins directory.

Code Block
For Linux/Mac: $ ./gradlew pullAllPluginsSource
For Windows: > gradlew pullAllPluginsSource

2.Checkout via GIT command(Following commands should be run from ofbiz-framework home)

For more details refer Apache OFBiz Source Repository page.

Running Apache OFBiz

  • Using the command line, build and start OFBiz (with demo data), use command:


Code Block
themeConfluence
For Linux/Mac: $ ./gradlew cleanAll loadAll ofbiz
For Windows: > gradlew cleanAll loadAll ofbiz

Above command will load demo data, (Sample Data to run apps) which comes with OFBiz, in Derby Database. Derby comes configured with OFBiz ready to use.

For more options see README.MD.

  • Direct your browser to https://localhost:8443/webtools and login with username "admin" and password "ofbiz" and look around a bit. 
    That's it, Apache OFBiz is now running on your system!!

Create Your First Application (Hello World...)

Introduction to Components

An OFBiz component is a folder, containing a special xml file, named “ofbiz-component.xml”, that describes the resources to be loaded and required by the component.
OFBiz itself is a set of components.

  • framework components: These are lower level components that provides the technical layer and tools to the application components; the features provided by these components are typically the ones provided by any other development framework (data layer, business logic layer, transaction handling, data source pools, etc…)
  • application components: These are generic business components required for ERP applications that can be extended/customized (product, order, party, manufacturing, accounting etc…); application components have access to the services and tools provided by the framework components and to the services published by other application components.
  • plugins components: These components are similar to application components but meant for special purpose applications like ecommerce, google base integration, eBay integration etc.

Create the plugin/component

It's very easy to setup a new custom component in OFBiz in plugins directory. Using the command line you just need run the following command.


Code Block
$ ./gradlew createPlugin -PpluginId=ofbizDemo


Image Added

Running your first application

Before running our first component, let's say 'Hello to the World'

  1. Simply open $OFBIZ_HOME/plugins/ofbizDemo/widget/OfbizDemoScreens.xml file from ofbizDemo plugin (you just created)


    Code Block
    languagexml
    <?xml version="1.0" encoding="UTF-8"?>
    <screens xmlns:xsi="

...

  1. http://www.w3.org/2001/XMLSchema-instance"

...

  1. 
            xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-screen.xsd">

...

  1. 
        <screen name="

...

  1. main">

...

  1. 
            <section>
                <actions>
                    <set field="headerItem" value="main"/><!-- this highlights the selected menu-item with name "main" -->
                </actions>
                <widgets>
                    <decorator-screen name="OfbizDemoCommonDecorator" location="${parameters.mainDecoratorLocation}">
                        <decorator-section name="body">
                            <label text="Hello World!! :)"/>
                        </decorator-section>
                    </decorator-screen>
                </widgets>
            </section>
        </screen>
    </screens>

    We have only added the <label text="Hello World!! :)" />

  2. Now you will need to restart OFBiz by reloading data(./gradlew loadAll ofbiz). It's required as you have created a new component with some security data for you component (Setup by default in your component data directory as OfbizDemoSecurityGroupDemoData.xml) and as you will restart it, ofbizdemo component will also be loaded.

  3. As OFBiz restarted direct your browser to your application here https://localhost:8443/ofbizDemo
  4. You will be asked to login. Login with user: admin password: ofbiz.
  5. As you login, you will see ofbizdemo application up with the hello world message you have put in screen as shown in below given image.

    Image Added

    That's it, congratulations your first component is setup and running!!

Creating First Database Entity (Table) 

Defining entity

To create custom Entities/Tables in database, you simply need to provide entity definition in  $OFBIZ_HOME/plugins/ofbizDemo/entitydef/entitymodel.xml file of your ofbizdemo application. This file structure is already setup when you used the Gradle task to setup your component. You simply need to go in and provide entity definition as shown below. Here we are going to add two new entities for ofbizdemo application.


Code Block
languagexml
<?xml version="1.0" encoding="UTF-8"?>
 
<entitymodel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/entitymodel.xsd">
 
    <title>Entity of an Open For Business Project Component</title>
    <description>None</description>
    <version>1.0</version>
 
    <entity entity-name="OfbizDemoType" package-name="org.apache.ofbiz.ofbizdemo" title="OfbizDemo Type Entity">
        <field name="ofbizDemoTypeId" type="id"><description>primary sequenced ID</description></field>
        <field name="description" type="description"></field>
        <prim-key field="ofbizDemoTypeId"/>
    </entity>
 
    <entity entity-name="OfbizDemo" package-name="org.apache.ofbiz.ofbizdemo" title="OfbizDemo Entity">
        <field name="ofbizDemoId" type="id"><description>primary sequenced ID</description></field>
        <field name="ofbizDemoTypeId" type="id"></field>
        <field name="firstName" type="name"></field>
        <field name="lastName" type="name"></field>
        <field name="comments" type="comment"></field>
        <prim-key field="ofbizDemoId"/>
        <relation type="one" fk-name="ODEM_OD_TYPE_ID" rel-entity-name="OfbizDemoType">
            <key-map field-name="ofbizDemoTypeId"/>
        </relation>
    </entity>
 
</entitymodel>

Now have a look at $OFBIZ_HOME/plugins/ofbizDemo/ofbiz-component.xml file. You already have resource entry made in it for loading these entities from their definitions to database when component loads. As shown below:


Code Block
languagexml
<entity-resource type="model" reader-name="main" loader="main" location="entitydef/entitymodel.xml"/>

To check simply re-start OFBiz (Ctrl+C followed by "./gradlew ofbiz") and direct your browser to Entity Data Maintenance Tool here: https://localhost:8443/webtools/control/entitymaint and search for entities OfbizDemoType and OfbizDemo. You will see it as shown in below given image.

Image Added


Preparing data for entity

As you have setup your custom entities, now is the time to prepare some sample data for it. You can do it in data XML files already setup under data directory of your component as  $OFBIZ_HOME/plugins/ofbizDemo/data/OfbizDemoTypeData.xml and  $OFBIZ_HOME/plugins/ofbizDemo/data/OfbizDemoDemoData.xml. Set it up as shown below:


Code Block
languagexml
titleOfbizDemoTypeData.xml
 <?xml version="1.0" encoding="UTF-8"?>
<entity-engine-xml>
    <OfbizDemoType ofbizDemoTypeId="INTERNAL" description="Internal Demo - Office"/>
    <OfbizDemoType ofbizDemoTypeId="EXTERNAL" description="External Demo - On Site"/>
</entity-engine-xml>


Code Block
languagexml
titleOfbizDemoDemoData.xml
<?xml version="1.0" encoding="UTF-8"?>
<entity-engine-xml>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_1" ofbizDemoTypeId="INTERNAL" firstName="Sample First 1" lastName="Sample Last 1" comments="This is test comment for first record."/>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_2" ofbizDemoTypeId="INTERNAL" firstName="Sample First 2" lastName="Sample last 2" comments="This is test comment for second record."/>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_3" ofbizDemoTypeId="EXTERNAL" firstName="Sample First 3" lastName="Sample last 3" comments="This is test comment for third record."/>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_4" ofbizDemoTypeId="EXTERNAL" firstName="Sample First 4" lastName="Sample last 4" comments="This is test comment for fourth record."/>
</entity-engine-xml>

Now again have a look at $OFBIZ_HOME/plugins/ofbizDemo/ofbiz-component.xml file. You already have resource entry made in it for loading data prepared in these files as:


Code Block
languagexml
titleEntry to be done in ofbiz-component.xml
<entity-resource type="data" reader-name="seed" loader="main" location="data/OfbizDemoTypeData.xml"/>
<entity-resource type="data" reader-name="demo" loader="main" location="data/OfbizDemoDemoData.xml"/>


Loading data in entity

At this moment to load this sample data into entities/tables defined you can either run ./gradlew loadAll on console or can directly go here in webtools to load entity xml https://localhost:8443/webtools/control/EntityImport.

Simply put your xml data in " Complete XML document (root tag: entity-engine-xml):" text area and hit "Import Text", as shown in below given image

Image Added

As you will hit Import Text, it will load data and will show the result as shown below

Image Added

After completing the data load process again visit Entity Data Maintenance(https://localhost:8443/webtools/control/entitymaint) and check your entities, you will find this data here that you just loaded.


That's it, you have successfully imported the data in the database tables, super easy, right!


Form and Services

In our previous section, we have seen how to create the entities (tables), now it's time to create a form which will allow you to make entries in that entity.

Create a Service

Before preparing form, let's write a service to create records in database for OfbizDemo entity in service definition xml file ($OFBIZ_HOME/plugins/ofbizDemo/servicedef/services.xml)



Code Block
languagexml
titleservices.xml
<?xml version="1.0" encoding="UTF-8"?>
<services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/services.xsd">
 
    <description>OfbizDemo Services</description>
    <vendor></vendor>
    <version>1.0</version>
 
    <service name="createOfbizDemo" default-entity-name="OfbizDemo" engine="entity-auto" invoke="create" auth="true">
        <description>Create an Ofbiz Demo record</description>
        <auto-attributes include="pk" mode="OUT" optional="false"/>
        <auto-attributes include="nonpk" mode="IN" optional="false"/>
        <override name="comments" optional="true"/>
    </service>
 
</services>

Now again have a look at $OFBIZ_HOME/plugins/ofbizDemo/ofbiz-component.xml file. You already have resource entry made in it for loading services defined in this file as:


Code Block
languagexml
<!-- service resources: model(s), eca(s) and group definitions -->
<service-resource type="model" loader="main" location="servicedef/services.xml"/>

For this service definition to load you will need to restart OFBiz. To test this service you directly go to webtools --> Run Service option here: https://localhost:8443/webtools/control/runService

Image Added

Info

Running service via Web Tools: This a smart utility provided by framework to run your service.

On submission of the form above, you will presented a form to enter IN parameters of the service.


Use of UI Labels (Introduction)

Internationalization of Apache OFBiz is really easy, we define the UI Labels in various languages and on the basis of user's locale, respective label is shown.

Here is the example of UI Labels (while creating component <component-name>UiLabels.xml is created by default, in our case it is OfbizDemoUiLabels.xml)


Code Block
languagexml
titleOfbizDemoUiLabels.xml
<?xml version="1.0" encoding="UTF-8"?>
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/ofbiz-properties.xsd">
    <property key="OfbizDemoApplication">
        <value xml:lang="en">OfbizDemo Application</value>
        <value xml:lang="zh">OfbizDemo应用程�?</value>
        <value xml:lang="zh-TW">OfbizDemo應用程�?</value>
    </property>
    <property key="OfbizDemoCompanyName">
        <value xml:lang="en">OFBiz: OfbizDemo</value>
        <value xml:lang="zh-TW">OFBiz: OfbizDemo</value>
    </property>
    <property key="OfbizDemoCompanySubtitle">
        <value xml:lang="en">Part of the Apache OFBiz Family of Open Source Software</value>
        <value xml:lang="it">Un modulo della famiglia di software open source Apache OFBiz</value>
        <value xml:lang="zh">开�?软件OFBiz的组�?部分</value>
        <value xml:lang="zh-TW">開�?軟體OFBiz的組�?部分</value>
    </property>
    <property key="OfbizDemoViewPermissionError">
        <value xml:lang="en">You are not allowed to view this page.</value>
        <value xml:lang="zh">�?�?许你�?览这个页�?�。</value>
        <value xml:lang="zh-TW">�?�?許您檢視這個�?�?�.</value>
    </property>
</resource>


Create the add Form

Let's create our first form for this service and for that let's edit the existing file at location $OFBIZ_HOME/plugins/ofbizDemo/widget/OfbizDemoForms.xml and add Create Form for OfbizDemo as shown below:



Code Block
languagexml
titleOfbizDemoForms.xml
<?xml version="1.0" encoding="UTF-8"?>
<forms xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-form.xsd">
 
    <form name="AddOfbizDemo" type="single" target="createOfbizDemo">
        <!-- We have this utility in OFBiz to render form based on service definition. 
             Service attributes will automatically lookedup and will be shown on form
        -->
        <auto-fields-service service-name="createOfbizDemo"/>
        <field name="ofbizDemoTypeId" title="${uiLabelMap.CommonType}">
            <drop-down allow-empty="false" current-description="">
                <!---We have made this drop down options dynamic(Values from db) using this -->
                <entity-options description="${description}" key-field-name="ofbizDemoTypeId" entity-name="OfbizDemoType">
                    <entity-order-by field-name="description"/>
                </entity-options>
            </drop-down>
        </field>
        <field name="submitButton" title="${uiLabelMap.CommonAdd}"><submit button-type="button"/></field>
    </form>
</forms>

Here you can notice we have used auto-fields-service to auto generate the form based on service definition IN/OUT attributes. 

Go to Screens xml file(OfbizDemoScreens.xml) add this form location in decorator body to your screen that you used to show the Hello World... text. As shown below


Code Block
languagexml
titleAdding Form Location to the Main Screen
 <?xml version="1.0" encoding="UTF-8"?>
<screens xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-screen.xsd">
 
    <screen name="main">
        <section>
            <actions>
                <set field="headerItem" value="main"/> <!-- this highlights the selected menu-item with name "main" -->
            </actions>
            <widgets>
                <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                    <decorator-section name="body">
                        <screenlet title="Add Ofbiz Demo">
                            <include-form name="AddOfbizDemo" location="component://ofbizDemo/widget/OfbizDemoForms.xml"/>
                        </screenlet>
                    </decorator-section>
                </decorator-screen>
            </widgets>
        </section>
    </screen>
</screens>


Controller Entry for Form

Before you go to the form and start creating OfbizDemo records from add form, you will need to make an entry in $OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/WEB-INF/controller.xml file for the target service which will called when form is submitted. You can do it as shown below under Request Mappings in your ofbizdemo apps controller file:


Code Block
languagexml
<request-map uri="createOfbizDemo">
    <security https="true" auth="true"/>
    <event type="service" invoke="createOfbizDemo"/>
    <response name="success" type="view" value="main"/>
</request-map>


Everything set, let's have a look into to our recently create form https://localhost:8443/ofbizDemo

Image Added

Primary key(ofbizDemoId) is not needed to be send in with the form, it will be auto sequenced by OFBiz in db records.


Create a Find Form

Let's create a find form for the entity OfbizDemo, so that you search OfbizDemos being created.

1.) Add the forms (FindOfbizDemo and ListOfbizDemo) in OfbizDemoForms.xml


Code Block
languagexml
titleOfbizDemoForms.xml
 <form name="FindOfbizDemo" type="single" target="FindOfbizDemo" default-entity-name="OfbizDemo">
    <field name="noConditionFind"><hidden value="Y"/> <!-- if this isn't there then with all fields empty no query will be done --></field>
    <field name="ofbizDemoId" title="${uiLabelMap.OfbizDemoId}"><text-find/></field>
    <field name="firstName" title="${uiLabelMap.OfbizDemoFirstName}"><text-find/></field>
    <field name="lastName" title="${uiLabelMap.OfbizDemoLastName}"><text-find/></field>
    <field name="ofbizDemoTypeId" title="${uiLabelMap.OfbizDemoType}">
        <drop-down allow-empty="true" current-description="">
            <entity-options description="${description}" key-field-name="ofbizDemoTypeId" entity-name="OfbizDemoType">
                <entity-order-by field-name="description"/>
            </entity-options>
        </drop-down>
    </field>
    <field name="searchButton" title="${uiLabelMap.CommonFind}" widget-style="smallSubmit"><submit button-type="button" image-location="/images/icons/magnifier.png"/></field>
</form>
 
<form name="ListOfbizDemo" type="list" list-name="listIt" paginate-target="FindOfbizDemo" default-entity-name="OfbizDemo" separate-columns="true"
    odd-row-style="alternate-row" header-row-style="header-row-2" default-table-style="basic-table hover-bar">
    <actions>
       <!-- Preparing search results for user query by using OFBiz stock service to perform find operations on a single entity or view entity -->
       <service service-name="performFind" result-map="result" result-map-list="listIt">
           <field-map field-name="inputFields" from-field="ofbizDemoCtx"/>
           <field-map field-name="entityName" value="OfbizDemo"/>
           <field-map field-name="orderBy" from-field="parameters.sortField"/>
           <field-map field-name="viewIndex" from-field="viewIndex"/>
           <field-map field-name="viewSize" from-field="viewSize"/>
        </service>
    </actions>
    <field name="ofbizDemoId" title="${uiLabelMap.OfbizDemoId}"><display/></field>
    <field name="ofbizDemoTypeId" title="${uiLabelMap.OfbizDemoType}"><display-entity entity-name="OfbizDemoType"/></field>
    <field name="firstName" title="${uiLabelMap.OfbizDemoFirstName}" sort-field="true"><display/></field>
    <field name="lastName" title="${uiLabelMap.OfbizDemoLastName}" sort-field="true"><display/></field>
    <field name="comments" title="${uiLabelMap.OfbizDemoComment}"><display/></field>
</form>


Form or Screen's action tag is used for data preparation logics for your view.


Info

We have used OOTB OFBiz generic service performFind to do the search operations which is easy and efficient to use when you have to perform search on one entity or one view entity.


2.) In next step, we will include these form in the screen, let's add these form in OfbizDemoScreens.xml file. For this include the FindOfbizDemo screen defined below in the OfbizDemoScreens.xml


Code Block
languagexml
 <!-- Find and list all ofbizdemos in a tabular format -->
<screen name="FindOfbizDemo">
    <section>
        <actions>
            <set field="headerItem" value="findOfbizDemo"/>
            <set field="titleProperty" value="PageTitleFindOfbizDemo"/>
            <set field="ofbizDemoCtx" from-field="parameters"/>
        </actions>
        <widgets>
            <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                <decorator-section name="body">
                    <section>
                        <condition>
                            <if-has-permission permission="OFBIZDEMO" action="_VIEW"/>
                        </condition>
                        <widgets>
                            <decorator-screen name="FindScreenDecorator" location="component://common/widget/CommonScreens.xml">
                                <decorator-section name="search-options">
                                    <include-form name="FindOfbizDemo" location="component://ofbizDemo/widget/OfbizDemoForms.xml"/>
                                </decorator-section>
                                <decorator-section name="search-results">
                                    <include-form name="ListOfbizDemo" location="component://ofbizDemo/widget/OfbizDemoForms.xml"/>
                                </decorator-section>
                            </decorator-screen>
                        </widgets>
                        <fail-widgets>
                            <label style="h3">${uiLabelMap.OfbizDemoViewPermissionError}</label>
                       </fail-widgets>
                    </section>
                </decorator-section>
            </decorator-screen>
        </widgets>
    </section>
</screen>

3.) Add request mapping for accessing this new Find Ofbiz Demo page in controller.xml


Code Block
languagexml
<!-- Request Mapping -->
<request-map uri="FindOfbizDemo"><security https="true" auth="true"/><response name="success" type="view" value="FindOfbizDemo"/></request-map>
  
<!-- View Mapping -->
<view-map name="FindOfbizDemo" type="screen" page="component://ofbizDemo/widget/OfbizDemoScreens.xml#FindOfbizDemo"/>


4.) Now, let's add a new menu for showing find option.
Creating a menu is really simple in OFBiz, all the menus are defined is *menus.xml.
When we create a component from a Gra, we get a file named OfbizDemoMenus.xml

Make the following entry in the OfbizDemoMenus.xml file.


Code Block
languagexml
titleOfbizDemoMenus.xml
<?xml version="1.0" encoding="UTF-8"?>
<menus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-menu.xsd">
    <menu name="MainAppBar" title="${uiLabelMap.OfbizDemoApplication}" extends="CommonAppBarMenu" extends-resource="component://common/widget/CommonMenus.xml">
        <menu-item name="main" title="${uiLabelMap.CommonMain}"><link target="main"/></menu-item>
        <menu-item name="findOfbizDemo" title="${uiLabelMap.OfbizDemoFind}"><link target="FindOfbizDemo"/></menu-item>
    </menu>
</menus>

Use of UI Labels (Completion)

As we have seen above Internationalization of Apache OFBiz is really easy, we define the UI Labels in various languages and on the basis of user's locale, respective label is shown.

Here we complete the example of UI Labels (while creating component <component-name>UiLabels.xml is created by default, in our case it is OfbizDemoUiLabels.xml)


Code Block
languagexml
titleOfbizDemoUiLabels.xml
 <property key="OfbizDemoFind">
    <value xml:lang="en">Find</value>
</property>
<property key="OfbizDemoFirstName">
    <value xml:lang="en">First Name</value>
</property>
<property key="OfbizDemoId">
    <value xml:lang="en">OFBiz Demo Id</value>
</property>
<property key="OfbizDemoLastName">
   <value xml:lang="en">Last Name</value>
</property>

Now simply restart the server, under ofbizdemo application (https://localhost:8443/ofbizDemo/control/main) you will see the Find menu option.

Image Added

Services using other engines

Whenever you have to build a business logic you should prefer to write services to leverage features from its built in Service Engine.

The service "createOfbizDemo" that you created earlier was using engine="entity-auto" and hence you didn't need to provide its implementation and OFBiz took care of create operation.  When you need to work on complex operations in service involving multiple entities from database and custom logics to be built, you need to provide custom implementation to your service. In this section we will focus on this.

Service in Java

You can implement a service in Java as directed here in below given steps:

1.) Define your service, here again we will be operating on the same entity(OfbizDemo) of our custom Ofbiz Demo application. Open your service definition file $OFBIZ_HOME/plugins/ofbizDemo/servicedef/services.xml and add a new definition as:


Code Block
languagexml
titleservices.xml
 <service name="createOfbizDemoByJavaService" default-entity-name="OfbizDemo" engine="java"
        location="com.companyname.ofbizdemo.services.OfbizDemoServices" invoke="createOfbizDemo" auth="true">
    <description>Create an Ofbiz Demo record using a service in Java</description>
    <auto-attributes include="pk" mode="OUT" optional="false"/>
    <auto-attributes include="nonpk" mode="IN" optional="false"/>
    <override name="comments" optional="true"/>
</service>


Info

Notice we have this time used engine="java".


2.) Create package "com.companyname.ofbizdemo.services" in your ofbizDemo components src/main/java directory (create those if they don't exist in your src directory). 

 Example: src/main/java/com/companyname/ofbizdemo/services. Services for your application which have to be implemented in Java can be placed in this java directory.

3.) Define new Java Class in file OfbizDemoServices.java here in services directory and implement method, which is going to be invoked by your service definition, as shown below:


Code Block
languagejava
titleOfbizDemoServices.java
package com.companyname.ofbizdemo.services;
import java.util.Map;
 
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.service.DispatchContext;
import org.apache.ofbiz.service.ServiceUtil;
 
public class OfbizDemoServices {
 
    public static final String module = OfbizDemoServices.class.getName();
 
    public static Map<String, Object> createOfbizDemo(DispatchContext dctx, Map<String, ? extends Object> context) {
        Map<String, Object> result = ServiceUtil.returnSuccess();
        Delegator delegator = dctx.getDelegator();
        try {
            GenericValue ofbizDemo = delegator.makeValue("OfbizDemo");
            // Auto generating next sequence of ofbizDemoId primary key
            ofbizDemo.setNextSeqId();
            // Setting up all non primary key field values from context map
            ofbizDemo.setNonPKFields(context);
            // Creating record in database for OfbizDemo entity for prepared value
            ofbizDemo = delegator.create(ofbizDemo);
            result.put("ofbizDemoId", ofbizDemo.getString("ofbizDemoId"));
            Debug.log("==========This is my first Java Service implementation in Apache OFBiz. OfbizDemo record created successfully with ofbizDemoId:"+ofbizDemo.getString("ofbizDemoId"));
        } catch (GenericEntityException e) {
            Debug.logError(e, module);
            return ServiceUtil.returnError("Error in creating record in OfbizDemo entity ........" +module);
        }
        return result;
    }
}


4.) Stop server and re-start using "./gradlew ofbiz", it will compile your class and will make it available when ofbiz restarts which updated jar file.

5.) Test service implemented using webtools --> Run Service option(https://localhost:8443/webtools/control/runService) or simply update the service name being called by your controller request to use this service instead and use add form in your app that you prepared earlier. By doing this your Add OfbizDemo form will call this java service.


Code Block
languagexml
<request-map uri="createOfbizDemo">
    <security https="true" auth="true"/>
    <event type="service" invoke="createOfbizDemoByJavaService"/>
    <response name="success" type="view" value="main"/>
</request-map>


To make sure this new service implementation is being executed, you can check this line in console log that you have put in your code using Debug.log(....). For logging in OFBiz you must always use Debug class methods in Java classes.


Code Block
languagejava
titleConsole Log
[java] 2014-06-24 12:11:37,282 (http-bio-0.0.0.0-8443-exec-2) [  OfbizDemoServices.java:28 :INFO] ==========This is 
my first Java Service implementation in Apache OFBiz. OfbizDemo record created successfully with ofbizDemoId: ......


Service in Groovy

To utilize feature of on the fly compilation and less line of code you can implement services for building business logics in OFBiz using Groovy DSL.

To implement a service using Groovy you can follow below given steps:

1.) Add new service definition to services/services.xml file as:


Code Block
languagexml
titleservices.xml
<service name="createOfbizDemoByGroovyService" default-entity-name="OfbizDemo" engine="groovy"
        location="component://ofbizDemo/groovyScripts/ofbizdemo/OfbizDemoServices.groovy" invoke="createOfbizDemo" auth="true">
    <description>Create an Ofbiz Demo record using a service in Groovy</description>
    <auto-attributes include="pk" mode="OUT" optional="false"/>
    <auto-attributes include="nonpk" mode="IN" optional="false"/>
    <override name="comments" optional="true"/>
</service>


2.) Add new groovy services file here  component://ofbizDemo/groovyScripts/ofbizdemo/OfbizDemoServices.groovy

3.) Add service implementation to the file OfbizDemoServices.groovy

Code Block
languagegroovy
titleOfbizDemoServices.groovy
import org.apache.ofbiz.entity.GenericEntityException;

def createOfbizDemo() {
    result = [:];
    try {
        ofbizDemo = delegator.makeValue("OfbizDemo");
        // Auto generating next sequence of ofbizDemoId primary key
        ofbizDemo.setNextSeqId();
        // Setting up all non primary key field values from context map
        ofbizDemo.setNonPKFields(context);
        // Creating record in database for OfbizDemo entity for prepared value
        ofbizDemo = delegator.create(ofbizDemo);
        result.ofbizDemoId = ofbizDemo.ofbizDemoId;
        logInfo("==========This is my first Groovy Service implementation in Apache OFBiz. OfbizDemo record "
                  +"created successfully with ofbizDemoId: "+ofbizDemo.getString("ofbizDemoId"));
      } catch (GenericEntityException e) {
          logError(e.getMessage());
          return error("Error in creating record in OfbizDemo entity ........");
      }
      return result;
}


4.) Stop server and re-start using"./gradlew ofbiz", this time we just need to load the new service definition, no explicit compilation is required as its a service implementation in Groovy.

5.) Test service implemented using webtools --> Run Service option(https://localhost:8443/webtools/control/runService) or simply update the service name being called by your controller request to use this service instead and use add form in your app that you prepared earlier for testing. By doing this your Add OfbizDemo form will call this groovy service.


Code Block
languagexml
titlecontroller.xml
<request-map uri="createOfbizDemo">
    <security https="true" auth="true"/>
    <event type="service" invoke="createOfbizDemoByGroovyService"/>
    <response name="success" type="view" value="main"/>
</request-map>


To make sure this new service implementation is being executed, you can check this line in console log that you have put in your code using Debug.log(....). For logging in OFBiz you must always use Debug class methods in Java classes.


Code Block
languagejava
titleConsole Log
[java] 2014-06-24 12:11:37,282 (http-bio-0.0.0.0-8443-exec-2) [  OfbizDemoServices.java:28 :INFO] ==========This is my 
first Groovy Service implementation in Apache OFBiz. OfbizDemo record created successfully with ofbizDemoId: .....


To get more details around using Groovy DSL for service and events implementation in Apache OFBiz you can refer document created by Jacopo Cappellato in OFBiz Wiki here.

Events

Events demonstration

Events in Apache OFBiz are simply methods used to work with HttpServletRequest and HttpServletResponse objects. You don't need to provide definitions of these as you did with services. These are directly called from controller. Events are also useful when you want to add custom server side validations to input parameters. For performing db operations you still call prebuilt services from events.

To write an event in OFBiz follow these steps:

1.) Add a new events directory to package and a new Events class file as mentioned here:


Code Block
languagejava
titleOfbizDemoEvents.java
package com.companyname.ofbizdemo.events;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.UtilMisc;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.service.GenericServiceException;
import org.apache.ofbiz.service.LocalDispatcher;
 
public class OfbizDemoEvents {
 
 public static final String module = OfbizDemoEvents.class.getName();
 
    public static String createOfbizDemoEvent(HttpServletRequest request, HttpServletResponse response) {
        Delegator delegator = (Delegator) request.getAttribute("delegator");
        LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
        GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
 
        String ofbizDemoTypeId = request.getParameter("ofbizDemoTypeId");
        String firstName = request.getParameter("firstName");
        String lastName = request.getParameter("lastName");
 
        if (UtilValidate.isEmpty(firstName) || UtilValidate.isEmpty(lastName)) {
            String errMsg = "First Name and Last Name are required fields on the form and can't be empty.";
            request.setAttribute("_ERROR_MESSAGE_", errMsg);
            return "error";
        }
        String comments = request.getParameter("comments");
 
        try {
            Debug.logInfo("=======Creating OfbizDemo record in event using service createOfbizDemoByGroovyService=========", module);
            dispatcher.runSync("createOfbizDemoByGroovyService", UtilMisc.toMap("ofbizDemoTypeId", ofbizDemoTypeId,
                    "firstName", firstName, "lastName", lastName, "comments", comments, "userLogin", userLogin));
        } catch (GenericServiceException e) {
            String errMsg = "Unable to create new records in OfbizDemo entity: " + e.toString();
            request.setAttribute("_ERROR_MESSAGE_", errMsg);
            return "error";
        }
        request.setAttribute("_EVENT_MESSAGE_", "OFBiz Demo created succesfully.");
        return "success";
    }
}


2.) Add controller request of calling this event as:


Code Block
languagexml
titlecontroller.xml
<request-map uri="createOfbizDemoEvent">
    <security https="true" auth="true"/>
    <event type="java" path="com.companyname.ofbizdemo.events.OfbizDemoEvents" invoke="createOfbizDemoEvent"/>
    <response name="success" type="view" value="main"/>
    <response name="error" type="view" value="main"/>
</request-map>


3.) Stop and start server by rebuilding it as we need to compile Java event class that we have added in #1.

4.) Now to test the event you can simply change the AddOfbizDemo form target to read "createOfbizDemoEvent" and as its submitted now it will call your event.

Difference between service and event

Here are some difference between services and events,

  • Events are used for validations and conversions using map processor, while services are used for business logics like CRUD operations.
  • Service returns Map.
  • Event returns String.
  • Services are loaded with the server, any changes in definition (not implementation if in MiniLang) needs a reload.
  • We can call service inside event. But we cannot call event inside service.
  • An event is specific local piece functionality normally used in one place for one purpose and called from its location.
  • A service is a piece of functionality which can be located anywhere on the network, is most of time used in several different places and is called by its 'name'.
  • In case of events you have access to HttpServletRequest and HttpServletResponse objects and you can read/write whatever you want. In case of services, you have access only to service parameters.

References: https://cwiki.apache.org/confluence/display/OFBIZ/FAQ+-+Tips+-+Tricks+-+Cookbook+-+HowTo#FAQ-Tips-Tricks-Cookbook-HowTo-DifferenceBetweenEventAndService and http://ofbiz.135035.n4.nabble.com

CriteriaServicesEvents
Require DefinitionYesNo
Implementation possibilitiesEntity auto, Java, Simple (XML) & GroovySimple (XML), Java & Groovy
Return TypeMapString
Used to write business logicYesNo
Job Scheduling possibleYesNo

Customizing User Interface

Using FreeMarker Template and Groovy Script

Okay so we are here in the last part of OFBiz tutorial. In this part we will focus on customizing UI layer of Apache OFBiz for business management apps i.e. backend apps and esp. Most of the time you will find the OFBiz Widgets are enough. But sometimes the important thing is to develop applications as users exactly want it. 

So to customize UI part of your application first of all to make it easy we will be using Freemarker Templates instead of inbuilt Form Widgets. First of all we will see how to use Freemarker and Groovy scripts with Apache OFBiz and then we'll see how to put on custom styling on it by defining your own decorators. Initially we will be using OFBiz default decorators.

Starting from here follow steps given:

1.) Add two Freemarker files at location $ OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/crud/AddOfbizDemo.ftl and ListOfbizDemo.ftl, as shown below:


Code Block
languagexml
titleAddOfbizDemo.ftl
<div class="screenlet-body">
  <form id="createOfbizDemoEvent" method="post" action="<@ofbizUrl>createOfbizDemoEvent</@ofbizUrl>">
    <input type="hidden" name="addOfbizDemoFromFtl" value="Y"/>
    <fieldset>
      <div>
        <span class="label">${uiLabelMap.OfbizDemoType}</span>
        <select name="ofbizDemoTypeId" class='required'>
          <#list ofbizDemoTypes as demoType>
            <option value='${demoType.ofbizDemoTypeId}'>${demoType.description}</option>
          </#list>
        </select>*
      </div>
      <div>
        <span class="label">${uiLabelMap.OfbizDemoFirstName}</span>
        <input type="text" name="firstName" id="firstName" class='required' maxlength="20" />*
      </div>
      <div>
        <span class="label">${uiLabelMap.OfbizDemoLastName}</span>
        <input type="text" name="lastName" id="lastName" class='required' maxlength="20" />*
      </div>
      <div>
        <span class="label">${uiLabelMap.OfbizDemoComment}</span>
        <input type="text" name="comments" id="comments" class='inputBox' size="60" maxlength="255" />
      </div>
    </fieldset>
    <input type="submit" value="${uiLabelMap.CommonAdd}" />
  </form>
</div>



Code Block
languagexml
titleListOfbizDemo.ftl
<div class="screenlet-body">
  <#if ofbizDemoList?has_content>
    <table cellspacing=0 cellpadding=2 border=0 class="basic-table">
      <thead><tr>
        <th>${uiLabelMap.OfbizDemoId}</th>
        <th>${uiLabelMap.OfbizDemoType}</th>
        <th>${uiLabelMap.OfbizDemoFirstName}</th>
        <th>${uiLabelMap.OfbizDemoLastName}</th>
        <th>${uiLabelMap.OfbizDemoComment}</th>
      </tr></thead>
      <tbody>
        <#list ofbizDemoList as ofbizDemo>
          <tr>
            <td>${ofbizDemo.ofbizDemoId}</td>
            <td>${ofbizDemo.getRelatedOne("OfbizDemoType").get("description", locale)}</td>
            <td>${ofbizDemo.firstName?default("NA")}</td>
            <td>${ofbizDemo.lastName?default("NA")}</td>
            <td>${ofbizDemo.comments!}</td>
          </tr>
        </#list>
       </tbody>
    </table>
  </#if>
</div>


2.) Add new Groovy file for data fetching logic at location $ OFBIZ_HOME/plugins/ofbizDemo/groovyScripts/crud/ListOfbizDemo.groovy and add code as shown to list out OfbizDemo records:


Code Block
languagegroovy
ofbizDemoTypes = delegator.findList("OfbizDemoType", null, null, null, null, false);
context.ofbizDemoTypes = ofbizDemoTypes;
ofbizDemoList = delegator.findList("OfbizDemo", null, null, null, null, false);
context.ofbizDemoList = ofbizDemoList;


3.) Add new screen file with Ofbiz default decorator to OfbizDemoScreens.xml with newly added freemarker and groovy files as:


Code Block
languagexml
titleOfbizDemoScreens.xml
<screen name="AddOfbizDemoFtl">
    <section>
        <actions>
            <set field="titleProperty" value="PageTitleAddOfbizDemos"/>
            <set field="headerItem" value="addOfbizDemoFtl"/>
            <script location="component://ofbizDemo/groovyScripts/crud/ListOfbizDemo.groovy"/>
        </actions>
        <widgets>
            <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                <decorator-section name="body">
                    <screenlet title="${uiLabelMap.OfbizDemoListOfbizDemos}">
                        <platform-specific>
                            <html><html-template location="component://ofbizDemo/webapp/ofbizDemo/crud/ListOfbizDemo.ftl"/></html>
                         </platform-specific>
                    </screenlet>
                    <screenlet title="${uiLabelMap.OfbizDemoAddOfbizDemoServiceByFtl}">
                        <platform-specific>
                            <html><html-template location="component://ofbizDemo/webapp/ofbizDemo/crud/AddOfbizDemo.ftl"/></html>
                        </platform-specific>
                    </screenlet>
                </decorator-section>
            </decorator-screen>
        </widgets>
    </section>
</screen>

4.) Add new controller request and a new item for OfbizDemo menu as:


Code Block
languagexml
titlecontroller.xml
<!--Request Mapping-->
<request-map uri="AddOfbizDemoFtl">
    <security https="true" auth="true"/>
    <response name="success" type="view" value="AddOfbizDemoFtl"/>
</request-map>
 
<!--View Mapping-->
<view-map name="AddOfbizDemoFtl" type="screen" page="component://ofbizDemo/widget/OfbizDemoScreens.xml#AddOfbizDemoFtl"/>


Code Block
languagexml
titleOfbizDemoMenus.xml
<menu-item name="addOfbizDemoFtl" title="${uiLabelMap.OfbizDemoAddFtl}"><link target="AddOfbizDemoFtl"/></menu-item> 

5.) Add new UI Labels as used by your app.

6.) Run your ofbiz demo application and go to the new tab you just added. You should have view as:

Image Added

Creating Custom Decorator

Having your UI in Freemarker gives you freedom to experiment it, doing CSS tweaks and make your application the way user wants. In this section we will see how we can do that.

We will be doing it by defining custom decorator for your application view. A decorator in OFBiz is nothing but a screen that you define and reuse afterwards by including in your other screens of application. You are already doing it with default decorator (main-decorator –> ApplicationDecorator) which comes with OFBiz. Just observe your screens you have prepared so far, you will find that, you were using this main decorator, please refer below line in OfbizDemoScreens.xml.

OfbizDemoScreens.xml


<decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">


Info

The mainDecoratorLocation is available in parameters map as it is defined in webapp's web.xml

web.xml


<context-param>
    <description>The location of the main-decorator screen to use for this webapp; referred to as a context variable in screen def XML files.</description>
    <param-name>mainDecoratorLocation</param-name>
    <param-value>component://ofbizDemo/widget/CommonScreens.xml</param-value>
</context-param>


Now is the time to define your own decorator with custom styling. 

In the sample given below we are going to use Bootstrap to style our sample Freemarker screen we developed in last part of this tutorial. Follow below given steps to build your own decorator.

1.) Download Bootstrap v3.3.7 directory, you can download it from here and unzip it.

2.) Create two new directories namely "css" and "js" at location $ OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/

3.) Copy bootstrap-3.3.7/dist/css/bootstrap.min.css to $ OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/css

4.) Copy bootstrap-3.3.7/dist/js/bootstrap.min.js to $ OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/js.

5.) Open $ OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/WEB-INF/web.xml and make entries for css and js directories in allowedPaths at the end as shown below:

web.xml


<init-param>
    <param-name>allowedPaths</param-name>
</init-param>


6.) Add new directory named "includes" at location $ OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/ and create two new files in this new directory you just added named PreBody.ftl and PostBody.ftl. We will be using(including) these two files in our decorator screen to build complete HTML page.


PreBody.ftl


<html>
  <head>
    <title>${layoutSettings.companyName}</title>
    <meta name="viewport" content="width=device-width, user-scalable=no"/>
    <#if webSiteFaviconContent?has_content>
      <link rel="shortcut icon" href="">
    </#if>
    <#list layoutSettings.styleSheets as styleSheet>
      <link rel="stylesheet" href="${StringUtil.wrapString(styleSheet)}" type="text/css"/>
    </#list>
    <#list layoutSettings.javaScripts as javaScript>
      <script type="text/javascript" src="${StringUtil.wrapString(javaScript)}"></script>
    </#list>
  </head>
  <body data-offset="125">
    <h4 align="center"> ==================Page PreBody Starts From Decorator Screen========================= </h4>
    <div class="container menus" id="container">
      <div class="row">
        <div class="col-sm-12">
          <ul id="page-title" class="breadcrumb">
            <li>
                <a href="<@ofbizUrl>main</@ofbizUrl>">Main</a>
            </li>
            <li class="active"><span class="flipper-title">${StringUtil.wrapString(uiLabelMap[titleProperty])}</span></li>
            <li class="pull-right">
              <a href="<@ofbizUrl>logout</@ofbizUrl>" title="${uiLabelMap.CommonLogout}">logout</i></a>
            </li>
          </ul>
        </div>
      </div>
      <div class="row">
        <div class="col-lg-12 header-col">
          <div id="main-content">
              <h4 align="center"> ==================Page PreBody Ends From Decorator Screen=========================</h4>
              <h4 align="center"> ==================Page Body starts From Screen=========================</h4>


PostBody.ftl


<#-- Close the tags opened in the PreBody section -->
          </div>
        </div>
      </div>
    </div>
    <h4 align="center"> ==================Page PostBody and Page body in general ends here from Decorator Screen=========================</h4>
  </body>
</html>


7.) Open Common Screens file of your component $ OFBIZ_HOME/plugins/ofbizDemo/widget/CommonScreens.xml, this is the file we will define our custom decorator.

8.) Update screen named "OfbizDemoCommonDecorator"(which will serve as custom decorator for your app) as shown below:

CommonScreens.xml


<screen name="OfbizDemoCommonDecorator">
    <section>
        <actions>
            <property-map resource="OfbizDemoUiLabels" map-name="uiLabelMap" global="true"/>
            <property-map resource="CommonUiLabels" map-name="uiLabelMap" global="true"/>
   
<set field="layoutSettings.companyName" from-field="uiLabelMap.OfbizDemoCompanyName" global="true"/>
            
<!-- Including custom CSS Styles that you want to use in your application view. [] in field can be used to
                 set the order of loading CSS files to load if there are multiple -->
            <set field="layoutSettings.styleSheets[]" value="/ofbizDemo/css/bootstrap.min.css"/>
   
            <!-- Including custom JS that you want to use in your application view. [] in field can be used to
                 set the order of loading of JS files to load if there are multiple -->
           <set field="layoutSettings.javaScripts[+0]" value="/ofbizDemo/js/bootstrap.min.js" global="true"/>
        </actions>
        <widgets>
            <section>
                <condition>
                    <if-has-permission permission="OFBIZDEMO" action="_VIEW"/>
                </condition>
                <widgets>
                    <platform-specific><html><html-template location="component://ofbizDemo/webapp/ofbizDemo/includes/PreBody.ftl"/></html></platform-specific>
                    <decorator-section-include name="pre-body"/>
                    <decorator-section-include name="body"/>
                    <platform-specific><html><html-template location="component://ofbizDemo/webapp/ofbizDemo/includes/PostBody.ftl"/></html></platform-specific>
                </widgets>
                <fail-widgets>
                    <label style="h3">${uiLabelMap.OfbizDemoViewPermissionError}</label>
                </fail-widgets>
            </section>
        </widgets>
    </section>
</screen>


In the code above you may have noticed the layoutSettings.styleSheets[] and layoutSettings.javaScripts[+0] notations. You can use the layoutSettings. notation for any files.

If you want to order styleSheets or javaScripts with empty square brackets you simply add the file at the end of the layoutSettings.styleSheets or layoutSettings.javaScripts list, with [+0] you add it at front of it.


9.) Use this decorator in your Freemarker screen that you created in last part as:

OfbizDemoScreens.xml


<screen name="AddOfbizDemoFtl">
    <section>
        <actions>
            <set field="titleProperty" value="OfbizDemoAddOfbizDemoFtl"/>
            <set field="headerItem" value="addOfbizDemoFtl"/>
            <script location="component://ofbizDemo/groovyScripts/crud/ListOfbizDemo.groovy"/>
        </actions>
        <widgets>
            <decorator-screen name="OfbizDemoCommonDecorator" location="${parameters.mainDecoratorLocation}">
                <decorator-section name="body">
                     <label style="h4" text="${uiLabelMap.OfbizDemoListOfbizDemos}"/>
                     <platform-specific>
                         <html><html-template location="component://ofbizDemo/webapp/ofbizDemo/crud/ListOfbizDemo.ftl"/></html>
                     </platform-specific>
                     <label style="h4" text="${uiLabelMap.OfbizDemoAddOfbizDemoFtl}"/>
                     <platform-specific>
                         <html><html-template location="component://ofbizDemo/webapp/ofbizDemo/crud/AddOfbizDemo.ftl"/></html>
                     </platform-specific>
                </decorator-section>
            </decorator-screen>
        </widgets>
    </section>
</screen>


10.) Update your FTL files to follow HTML web standards and apply CSS on it as:

AddOfbizDemo.ftl


 <form method="post" action="<@ofbizUrl>createOfbizDemoEventFtl</@ofbizUrl>" name="createOfbizDemoEvent" class="form-horizontal">
  <div class="control-group">
    <label class="control-label" for="ofbizDemoTypeId">${uiLabelMap.OfbizDemoType}</label>
    <div class="controls">
      <select id="ofbizDemoTypeId" name="ofbizDemoTypeId">
        <#list ofbizDemoTypes as demoType>
          <option value='${demoType.ofbizDemoTypeId}'>${demoType.description}</option>
        </#list>
      </select>
    </div>
  </div>
  <div class="control-group">
    <label class="control-label" for="firstName">${uiLabelMap.OfbizDemoFirstName}</label>
    <div class="controls">
      <input type="text" id="firstName" name="firstName" required>
    </div>
  </div>
  <div class="control-group">
    <label class="control-label" for="lastName">${uiLabelMap.OfbizDemoLastName}</label>
    <div class="controls">
      <input type="text" id="lastName" name="lastName" required>
    </div>
  </div>
  <div class="control-group">
    <label class="control-label" for="comments">${uiLabelMap.OfbizDemoComment}</label>
    <div class="controls">
      <input type="text" id="comments" name="comments">
    </div>
  </div>
  <div class="control-group">
    <div class="controls">
      <button type="submit" class="btn">${uiLabelMap.CommonAdd}</button>
    </div>
  </div>
</form>


ListOfbizDemo.ftl


<table class="table table-bordered table-striped table-hover">
    <thead>
        <tr>
          <th>${uiLabelMap.OfbizDemoId}</th>
          <th>${uiLabelMap.OfbizDemoType}</th>
          <th>${uiLabelMap.OfbizDemoFirstName}</th>
          <th>${uiLabelMap.OfbizDemoLastName}</th>
          <th>${uiLabelMap.OfbizDemoComment}</th>
        </tr>
    </thead>
    <tbody>
        <#list ofbizDemoList as ofbizDemo>
            <tr>
              <td>${ofbizDemo.ofbizDemoId}</td>
              <td>${ofbizDemo.getRelatedOne("OfbizDemoType").get("description", locale)}</td>
              <td>${ofbizDemo.firstName?default("NA")}</td>
              <td>${ofbizDemo.lastName?default("NA")}</td>
              <td>${ofbizDemo.comments!}</td>
            </tr>
        </#list>
    </tbody>
</table


10. Now restart OFBiz as you have made entries to allowedPaths in web.xml. As it reloads hit https://localhost:8443/ofbizDemo/control/AddOfbizDemoFtl you should see page with custom styles that you have used instead of using default OFBiz theme. It should look like:

Image Added

Here you can now play with it as you want. Try changing header or having new one, adding footer, putting in validations etc. So this way you can customize UI layer of OFBiz with Freemarker templates, CSS and JS.

You may want to add your own CSS or JS files, you can include those the same way we did for Bootstrap files.

Whats next?

</screens> 

Step - 9 : Now that we have the basic elements in place lets review the basic flow no matter how large or complex your component gets. First a request will be made by a browser to see a specific resource. Take for example the request:

"localhost:8080/practice/control/main"

1- When ofbiz sees this request it will look at the /practice section first. Because in our ofbiz-component.xml file we said our webapps mount point would be /practice. Now ofbiz knows that our practice component will handle the rest of the request.

2- Ofbiz will then look at our controller.xml file. Inside our controller.xml file we have request-maps and view-maps. If it finds a view-map with the name 'main' it will go there. The request-map can either specify a view, or as we will see later an event or a service. If it specifies a view it will look further down in the controller.xml file and see if there is a view-map with the name specified by the value tag in the request-map.

3- For now we will keep it simple and assume that all the views go to a type=screen. If this is the case then the page tag will specify a path to a screen definition file as well as a screen name to display after the "#" sign.

*Step - 10 :*Now its the time to run you first practice application :
                Start server by : java -Xmx256M -jar ofbiz.jar (the -Xmx256M command just ensures that the program has enough memory)
Then hit the url http://localhost:8080/practice/control/main in your browser.
                Browser should show "This is first practice"

Step - 11 : Now create a file in webapp directory "practice" by name index.jsp (Contents of this file can be taken from "example" component), this file is responsible for redirecting the response to control/main if you give url  http://localhost:8080/practice/ and if you give url like http://localhost:8080/practice/unknown/requestit will be redirected to the redirectPath specified in web.xml. In that case, ContextFilter filter out the request and use this redirect path to redirect the request.

Doing some advancements :

Step - 1 : Now its the time to create a decorator for the screens in this application so for that create a file by name CommonScreens.xml file in "widget" directory. This file will be containing the common screens which will be used through out the whole application. E.g. A common screen may have header and footer included in it so which ever is the screen will be using it as a decorator will be having those in common. For this you can take reference from CommonScreens.xml file of "example" component.
An important reading at this moment is at http://docs.ofbiz.org/display/OFBIZ/Understanding+the+OFBiz+Widget+Toolkit another is http://docs.ofbiz.org/display/OFBIZ/FAQ+-+Tips+-+Tricks+-+Cookbook+-+HowTo  in this you can read the contents under heading  "The Decorator".

Step - 2 : Create a menu for this application. For this create a file by name PracticeMenus.xml in "widget" directory of you component. For this take a reference from ExampleMenus.xml file of "example" component.

Step - 3:  Create the sub-directory "actions" inside the WEB-INF directory.
               In this directory we will create the scripting files. Scripting files are used to prepare data on fly. These files will be bsh files.
Reference : http://www.beanshell.org/docs.html. While working in bsh always be conscious about the imported classess and packages. Import only those which have been used in your file. For putting log messages use methods from "Debug" class just do this practice from the begining itself.
so create a file by name person.bsh in this  actions directory which will be fetching all the records from the entity "Person".

Now a new scripting language has been introduced in place of bsh script the name is "groovy". For more info you can visit : http://groovy.codehaus.org/ 

An important reading for this is at : Tips & Tricks while working with Groovy

Step - 4 : Now in webapp "practice" create one ftl file by name "person.ftl" which will be used to show the data fetched by bsh file.
               Reference : http://freemarker.sourceforge.net/docs/

Step - 5 : Now create a new screen by name "person" in PracticeScreens.xml file and also create a new menu item in PracticeMenus.xml file.

Step - 6 : Now do the needful for rendering this screen in controller.xml file as we did earlier.

Now again run the application and see the results.

Now moving to create a form for showing the content of Person entity on the screen:(Using Form Widget)

Step - 1 :  Now add one more menu item to by name "PersonForm" to your PracticeMenus.xml file.

Step - 2: Create one file in widget by name PracticeForms.xml and create a list form for showing the records from person entity.
(Take reference from ExampleScreens.xml and ExampleForms.xml files).

Step - 3 :  Create new screen by name personForm and include this list form in it.

Step - 4 : Do the needful for showing this screen in controller.xml.

Now run the application again and observe the difference.

Till Now you have worked on controller requests mappings, Screen widget, form widget, Decorator, Menus, bsh, ftl.

Create main Decorator for decorating this application:

Step - 1 : Create screen by name "main-decorator" in CommonScreens.xml file.(Take reference from CommonScreens.xml file of Example component.)

Step - 2 : Now include this decorator in CommonPracticeDecorator screen which you are already having.

Now run it again and see the difference.

Now its the time to show practice application in the app bar :

Step - 1 :  For this just make app-bar-display="true" in ofbiz-component.xml file.

             Restart the server then run it again you will find practice application in app bar.

Create UI Labels:

Step - 1 :  For this create directory by name "config" in your component directory i.e. "practice".

Note: -Here remember to create an entry for the config directory in your ofbiz-component.xml file.

           which will be : <classpath type="dir" location="config"/> means you have to place the config directory on the classpath to access configuration files.

Step - 2: Now create a file by name PracticeUiLabels.xml and create some of the ui labels for practice applicaton. (take reference from ExampleUiLabels.xml). Here remember one thing whenever you make   a  change in UiLabels then you have to restart the server for having the changes in effect. At first create only 2 ui labels like

    <property key="PracticeApplication">
        <value xml:lang="en">This is first practice</value>
    </property>
    <property key="PracticeCompanyName">
        <value xml:lang="en">OFBiz: Practice</value>
    </property>

Step - 3: Include this Ui Label resource in your main decorator screen which you created earlier and use these one or two Ui labels which you are having now.

Step - 4 : Use those 2 ui labels at appropriate places.

Note : Always search first for any existing Ui label in ofbiz and if you don't find it there then only create new one.

Now its time to make this practice application secure by checking authentication (user login):

Step - 1 :  Take reference from ExampleMenus.xml file for having login and logout options in your menu.

             Targets for these options will be available from "component://common/webcommon/WEB-INF/common-controller.xml", which we have to include in our controller.xml. 

Step - 2 :  Make changes in requests in controller.xml file make auth="true" means now these requests needs authentication.

             This is first security level which you have implemented.

Now run your application and observe the difference.

Writing CRUD operations:

Create, Update and Delete operations for an entity will be done by services which we will be writing in minilang.

At first approach we will write our own services for performing these operations for making a better understanding with it.

Onwards we will be doing this by calling already implemented services.

For doing so we will take the entities from Party model which are:

--Party

--Person

A person is a party so for the creation of a person first a party needs to be created with partyTypeId="PERSON".

So there can be two ways to do that:

1. Create service for the creation of a party with type Person.

2. Create a party first in the service which will be creating person.

For writing services following steps must be performed:

Step - 1: Create directory by name "servicedef" in component directory "practice". This directory will contain all the service definition files e.g. services.xml, secas.xml.

Note If it is a service which is written in Java then it will be placed in "src" directory and if it is a service which is written in minilang then it will be placed in script directory. e.g. for java applications/party/src/org/ofbiz/party/party/PartyServices.java and for minilang applications/party/script/org/ofbiz/party/party/PartyInvitationServices.xml. Respective class path and file path will be mentioned in the service definition.

Step - 2 :  In controller you have to create an entry for the request for the execution of a service and set the response.

Step - 3: Now all the services which you have written needs to be loaded when server starts so you need to do an entry for service definition in ofbiz-component.xml file which will be like :

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

So whenever you make any change in any service definition then you must restart the server to have changes in effect.

Writing CRUD operations for Party entity:

First we will be writing services for Party then while writing services for creating Person we will be calling the service for party.

Step - 1 :  Create a file by name "services.xml" in servicedef directory.

Step - 2 : Define services for CRUD operations for Party entity. Name of services will be createPracticeParty, updatePracticeParty, deletePracticeParty and specify the correct location to the file where these services will be implemented like /framework/example/script/org/ofbiz/example/example/ExampleServices.xml.

Step - 3 : Create directory structure and PracticeServices.xml file in your component directory for giving the implementation of these services.
             (For implementation take reference from service.xml and ExampleServices.xml files of Example component)

Note : Do not use the <override> tag as it is introduced later in the tutorial. 

From this place if you want to run these services then you can run them by webtools--> Run Service . By this place you can test your services.

Writing CRUD operations for Person entity:

-- Here for the creation of record for person entity we will need to have the partyId for that so we will first call the service createPracticeParty then after getting the partyId we will create the record for person.

-- Here we will be adding one add form in the bottom of the list form which we have for the person entity. This form will be calling the services for creating a record for person.

Step - 1 : Create the add form for the creation of person and add this in the same screen for person form.

Step - 2 :  Write CRUD operations for person entity.

Step - 3: Now convert the List form with editable field(Ref. ListExampleItems from ExampleForms.xml) and add Update and delete option with it and also in the same screen there is add form also.

Step - 4 :  Create controller entries for these services which are going to be called by this form.

Writing Events:

Events can be written in Java and minilang both. Now the next development which you are going to do will be writting these events.

Events are used for the validation and conversion using Simple Map Processor. The Simple Map Processor Mini-Language performs two primary tasks: validation and conversion. It does this in a context of moving values from one Map to another. The input map will commonly contain Strings, but can contain other object types like Integer, Long, Float, Double, java.sql.Date, Time, and Timestamp.

Before moving any further an important link to go through is : http://docs.ofbiz.org/display/OFBIZ/Mini-Language+Guide#Mini-LanguageGuide-smapFor making an understanding with it implementation will be done by performing following steps:

Step - 1 : For this create another tab in your practice application menu bar for this by Name "Events".

Step - 2 : Now create another menu with two menu item in PracticeMenus.xml file by name "EventMenu". This menu will be having 2 menu Item one will be by name "EventMinilang" and  another by name "EventJava". One will be used to show the form which we will be calling an event which will be in minilang and other will be used to call java event.

Step - 3 : Simply show form on the request of both which will be for creating a new person. Both the forms will be different for calling different events as target in them.

Step - 4 : Show labels in screens above the form like "New Person -- Simple Event"  and  "New Person -- Java Event" so that it will be easy to identify the purpose of that form.

Step - 5 : Now set event in target of the forms and create request mappings in controller for the event.

Here important thing to note is in case of simple event controller entry will be like :

<request-map uri="createPracticePersonSimpleEvent">
        <security https="true" auth="true"/>
        <event type="simple" path="org/hotwax/practice/PracticeEvents.xml" invoke="createPracticePersonSimpleEvent"/>
        <response name="success" type="view" value="CreatePracPersonSimpleEvent"/>
        <response name="error" type="view" value="CreatePracPersonSimpleEvent"/>
</request-map>

Here the path is the path of the file where the event is written. it will be practice/script/org/hotwax/practice.

 and for java event controller entry will be like:

<request-map uri="createPracticePersonJavaEvent">
        <security https="true" auth="true"/>
        <event type="java" path="org.hotwax.practice.PracticeEvents" invoke="createPracticePersonJavaEvent"/>
        <response name="success" type="view" value="CreatePracPersonJavaEvent"/>
        <response name="error" type="view" value="CreatePracPersonJavaEvent"/>
</request-map>

Here the path is the classpath in which this event is defined. 

The file name will be PracticeEvents.java and will be creted at  practice/src/org/hotwax/practice.

Simple Event 

Step - 6 :  Now in the script/org/hotwax/practice/ create one file by name PracticeEvents.xml.

Step - 7 : Write the event in PracticeEvents.xml file by name createPracticePersonSimpleEvent.(For reference you can go through the event "createUser" from UserEvents.xml from party component)

The event which you will be writting should be the simple one as you just have to process 5 fields comming from the form which are salutation, firstName, middleName, lastName, suffix. and then you have to call the createPracticePerson service.

For processing the field you will be using simple map processor as you have read earlier. 

Follow these steps for writting the event:

7.a : Process fields coming from the form like: 

     <call-map-processor in-map-name="parameters" out-map-name="createPersonContext">
         <simple-map-processor name="createPersonMap">
             <process field="firstName">
                 <copy/>
                 <not-empty>
                     <fail-property property="PracticeFirstNameMissingError" resource="PracticeUiLabels"/>
                 </not-empty>   
             </process>
         </simple-map-processor>
     </call-map-processor>
     <check-errors/>

7.b : Create some Ui labels for showing them in fail-property like PracticeFirstNameMissingError.

7.c : Now call service createPracticePerson service by passing out map which is obtained after processing fields as a in map to the service.

Java Event: 

Here the java event which you will be writting will be fairly simple. For reference you can check any of the *Events.java file.

Step - 1 : The contents can be :

Code Block

public static String createPracticePersonJavaEvent(HttpServletRequest request, HttpServletResponse response){         LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");       GenericDelegator delegator = (GenericDelegator) request.getAttribute("delegator");
}

Step - 2 : Now you have to process the fields comming from the form like :            

Code Block

String salutation = (String) request.getParameter("salutation");
String firstName = (String) request.getParameter("firstName");

Step - 3 : Now prepare a map for the values which you have to pass to the service which you will call "createPracticePerson" . Like :            

Code Block

Map createPersonCtx = UtilMisc.toMap("salutation", salutation, "firstName", firstName);

Step - 4 : Then at the end just call the service "createPracticePerson" like :             

Code Block

try{
    Map person = dispatcher.runSync("createPracticePerson", createPersonCtx);
 }catch (GenericServiceException e){
     Debug.logError(e.toString(), module);          return "error";
 }
return "success";

After writting event in Java don't forget to compile it by running "ant" command. At this moment you will need to add build.xml file to your component directory i.e. at hot-deploy/practice/ For the content of build.xml file you can refer "example" component.
Here in build.xml file ensure one thing you are having follwing entry:

Code Block

<target name="classpath">
    <path id="local.class.path">
        <fileset dir="../../framework/base/lib/j2eespecs" includes="*.jar"/>
    </path></target>

This will needed for the classes like

Code Block

import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

So create a file by name build.xml and then compile it. It will create a build directory in your component directory which will be containing all *.jar and class files after compilation. For the content of build.xml file you can refere example component. 

For running the simple event don't forget to make an entry for <classpath type="dir" location="script"/> in ofbiz-component.xml file.

For running the java event make an entry <classpath type="jar" location="build/lib/*"/> in ofbiz-component.xml file.

ECA(Event Condition Action): 

ECA : Its a combinition of 3 things an event, conditions per event and Actions per event. It is a rule. Which is used to trigger an action on the execution of an event when certain conditions are met. 

This is much like a trigger. When a service is called, a lookup is performed to see if any ECAs are defined for this event. Events include before authentication, before IN parameter validation, before actual service invocation, before OUT parameter validation, before transaction commit, or before the service returns. Next each condition in the ECA definition is evaluated and if all come back as true, each action is performed. An action is just a service which must be defined to work with the parameters already in the service's context. There are no limit to the number of conditions or actions each ECA may define.

For mor details on this visit :  http://docs.ofbiz.org/display/OFBTECH/Service+Engine+Guide

1. SECA (Service Event Condition Action) : This is used when we want to trigger another service(action) on the execution of a service when certain conditions are met.

2. EECA (Entity Event Condition Action) : This used when we want to trigger a service on the creation of a record for an entity when certain conditions are met.

For the implementation of ECA again we will be following the same approach for screens, menus by following steps:

Step - 1 : Add one more application menu in practice application's menu bar by name "ECA".(Do the needful entries for target in controller.xml)

Step - 2 : Now create another menu with two menu item in PracticeMenus.xml file by name "EcaMenu". This menu will be having 2 menu Item one will be by name "seca" and "eeca". For both of them 2 screens will be needed which will be using the form "CreatePerson" which we are already having. (in personForm screen)

SECA :

Step - 1 : For this you have to write another service by name "createPartyRoleVisitor",  which will be setting the role for the party which will be created by "createPracticePerson" service.
The service "createPartyRoleVisitor" will be triggered by the seca rule which you will define for service "createPracticePerson".
In the new service involved entity will by "PartyRole". In "createPartyRoleVisitor" just call service "createPartyRole" which is already implemented.

Step - 2 :  Now you have to create a file by name "secas.xml" in "servicedef" directory. Seca definition will come here. (Take reference from secas.xml of "party" component). This will be

<eca service="createPracticePerson" event="commit">
    <condition field-name="partyId" operator="is-not-empty"/>
    <action service="createPartyRoleVisitor" mode="sync"/>
</eca>

Step - 3 : Do an entry in ofbiz-component.xml file for this seca definition to to be loaded. It will be :

<service-resource type="eca" loader="main" location="servicedef/secas.xml"/>

Don't forget to restart the server after doing this entry.

Now run the service through form and check the records in PartyRole entity. You will find a role is set for the party created because synchrounously you have triggered another service by seca rule for setting up the role for the party created.

EECA :

Step - 1 : For this you have to write another service by name "createPartyRoleCustomer",  which will be setting the role for the party which will be created means when a record for the entity "Party" will be created this service will be triggered for setting a role customer for that party. The service "createPartyRoleCustomer" will be similar to "createPartyRoleVisitor".

Step - 2 :  Now you have to create a file by name "eecas.xml" in "entitydef" directory, which will be in your component directory "practice". Eeca definition will come here. (Take reference from eecas.xml of "accounting" component). This will be :

    <!- To create party role whenever a party is created ->
    <eca entity="Party" operation="create" event="return">
        <condition field-name="partyId" operator="is-not-empty"/>
        <action service="createPartyRoleCustomer" mode="sync"/>
    </eca>

Step - 3 : Do an entry in ofbiz-component.xml file for this seca definition to to be loaded. It will be :

<entity-resource type="eca" reader-name="main" loader="main" location="entitydef/eecas.xml"/>

 Don't forget to restart the server after doing this entry.

Now run the service through form and check the records in PartyRole entity. You will find a role is set for the party created because synchrounously you have triggered a service by eeca rule for setting up the role for the party created.

The main difference here is that you are triggering a service when an operation performed on the entity. In our case it is "create".

Note Here you have created a saparate menu to understand the concept separately. As you written seca for the service "createPracticePerson", so where ever in your practice application you will be calling this service that seca will trigger "createPartyRoleVisitor" and on the other hand when a party will be created "createPartyRoleCustomer" will be triggered.

 Group Service:

    Group services are used to call more then one services as a group. Service groups are a set of services which should run when calling the initial service. You define a service using the group service engine, and include all the parameters/attributes needed for all the services in the group. The location attribute is not needed for groupservices, the invoke attribute defines the name of the group to run. When this service is invoked the group is called and the services defined in the group are called as defined.

For mor details on this visit :  http://docs.ofbiz.org/display/OFBTECH/Service+Engine+Guide

For the implementation of Group service follow these steps: 

Step - 1 :  Add another menu item to applicatoin menu bar by name "Group Service".(Do the needful entries for target in controller.xml)

Step - 2 : Now create new screen and a form for creation of the person because the target for the form will be the group service which we will be defining.

Note : Now the time is to define the group service. We will be defining the group service for the services which we have implemented for this practice application.

Step - 3 : You will be defining the service group in services.xml file.(Take reference from services.xml of party component).

Just write one more service which will be setting the role "CLIENT" for the party which will be created by createPracticePerson Service. 

 Create a group service by name "partyGroup" like :

<!-- Group service -->
    <service name="partyGroup" engine="group" auth="true">
        <description>Creates a party, person and party role Client</description>
        <group>
            <invoke name="createPracticePerson" result-to-context="true"/>
            <invoke name="createPartyRoleClient"/>
        </group>
    </service>

Don't forget to restart the server before testing it. 

Interface:

The interface service engine has been implemented to help with defining services which share a number of the same parameters. An interface service cannot be invoked, but rather is a defined service which other services inherit from. Each interface service will be defined using the interface engine.

For more details on this visit :  http://docs.ofbiz.org/display/OFBTECH/Service+Engine+Guide

For implemeting the interface follow these steps:

Step - 1 : Add another menu item to applicatoin menu bar by name "Interface".(Do the needful entries for target in controller.xml)

Step - 2 : Create new screen, form and service for creating a person. Here service will implement the interface. (For creating interface take reference from services_fixedasset.xml of accounting component) it will be like :

<!-- Peson Interface -->
    <service name="createPersonInterface" engine="interface" location="" invoke="">
        <attribute name="firstName" mode="IN" type="String" optional="false"/>
        <attribute name="middleName" mode="IN" type="String" optional="true"/>
        <attribute name="lastName" mode="IN" type="String" optional="false"/>
        <attribute name="suffix" mode="IN" type="String" optional="true"/>
    </service>
   
    <service name="createPracticePersonInterfaceService" engine="simple" location="org/hotwax/practice/PracticeServices.xml" invoke="createPracticePersonInterfaceService" auth="false">
        <description>Creates a new Person</description>
        <implements service="createPersonInterface"/>
        <attribute name="partyId" mode="OUT" type="String" optional="false"/>  
        <override name="suffix" optional="false"/>
    </service>

Here we are implementing an interface and overriding the behaviour of the attribute "suffix", which will have effect when this service will be in action.

Implementation of service createPracticePersonInterfaceService will be the same as createPracticePerson.

Don't forget to restart the server after this implementation.

Creating new entity:

 For the creation of new entity you can again take a referecne from example component for this you can have a look in entitymodel.xml file of example component. You can create new entities by following these steps:

Step - 1 : Create a new subdirectory by name entitydef in hot-deploy/practice/.

Step - 2 : Create new file by name  entitymodel.xml. This file will contain the defintions of entities which you want to define.

Step - 3 : For loading the defintion you need to do an entry in your ofbiz-component.xml file like:

Code Block

    <entity-resource type="model" reader-name="main" loader="main" location="entitydef/entitymodel.xml"/>

           That implies that when ever you do a change you need to restart the server to have those changes in effect.

At this place an important reading is at http://docs.ofbiz.org/display/OFBTECH/General+Entity+Overview.

You will rarely find this way to define new entity because you are already having entities there in OFBiz already defined which will be useful for the conduction of your business process. Though you may feel at some place to add more fields to an existing entity so how can you do that? The next  step will show you the way how you can extend an entity for your customized needs.

Earlier we used to have one more file in same directory by name entitygroup.xml which not needed any more because code is checked in to the trunk for this.

Extending an existing entity:

Yes you can extend an existing entity for adding more fields to for your custom needs. This can be done in following way:

Step - 1 :  For extending an entity use

Code Block

    <extend-entity entity-name="">
        <field name="" type=""/>
    </extend-entity>

This is the simplest form it can be more complex. This will add up one more field to the entity you already have. Now it depends which field you want for your custom needs. Here you can also defined relation of this field with other entities you want. But before doing this you should search extesively may be you will be adding a field for a purpose and there is already a field which will serve the purpose, so be concisous about this. Also go for a extensive study of data model then do this.
 

For entity engine configuration dont forget to read : Entity Engine Configuration Guide
 

Conclusion:

If you have followed all the steps and developed practice application from this application tutorial then this will really help you in understanding other implementations implementation in OFBiz. These things are basic foundation of working in OFBiz. Now you know that , how you can start the development in OFBiz. Don't leave behind the extra links provided in this tutorial as they will really help you a lot in understanding the things which are there. Parallaly with the development of this application you can watch OFBiz videos which are available at Framework Introduction Videosgiven there in detail.
Here is another good reading will can be of help you a lot is available at FAQ Tips Tricks Cookbook HowTo .
Now the next thing comes in mind the way is the business process processes which is are really needed to work onbe understood well for understanding OOTB process flow in OFBiz and OOTB data model, so for this, books are available at : http://docs.ofbiz.org/display/OFBADMIN/OFBiz+Related+Books\\Image Removed OFBiz Related Books. Understanding well the OFBiz OOTB available data model and business processes will help in building better business solutions top of it.

Now you are ready to dive in. Welcome to OFBiz world.

Children Display
alltrue