Versions Compared

Key

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

...

This document is meant for OFBiz  beginners. It will help to come up with the most basic knowledge of OFBiz application development process. Aim behind the creation of this tutorial is to acquaint a developer with best practices, coding conventions and the control flow and many more.

Part - 1 

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 - 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 - 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):

Code Block
<?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"
       base-permission="OFBTOOLS"
       location="webapp/practice"
       mount-point="/practice"
       app-bar-display="false"/>
</ofbiz-component>

...

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:

Code Block
<?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>

...

All these readings will be really of help.
Very first screen will be like :

Code Block
<?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>
            <widgets>
                <label text="This is first practice"/>
            </widgets>
        </section>
    </screen>

</screens>

...

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 groovy files. Earlier we were using bsh(beanshell) files. This is a script which is used to fetch the data from database on the fly for the UI.
Reference : Tips & Tricks while working with Groovy & http://groovy.codehaus.org/. While working in groovy always be conscious about the imported classes 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 beginning itself. So create a file by name Person.groovy in this actions directory which will be fetching all the records from the entity "Person". At this moment this is really going to be the small code for doing this (a single line) like

Code Block
context.persons = delegator.findList("Person", null, null, null, null, false);

...

Step - 4 : Now in webapp "practice" create one ftl file by name "Person.ftl" which will be used to show the data fetched by groovy file.
Reference : http://freemarker.sourceforge.net/docs/
At this moment you need only to iterate the list of persons which is there in the context. The only code that is needed to that is:

Code Block
<#if persons?has_content>
  <h2>Some of the people who visited our site are:</h2>
  <br>
  <ul>
    <#list persons as person>
      <li>${person.firstName?if_exists} ${person.lastName?if_exists}<li>
    </#list>
  </ul>
</#if>

...

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

which will be :

Code Block
<classpath type="dir" location="config"/>

...

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

Code Block
<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 - 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.
or you can do these entries in your controller.xml file under

Code Block
<!- Request Mappings ->
<!-- Security Mappings -->
 <request-map uri="checkLogin" edit="false">
    <description>Verify a user is logged in.</description>
        <security https="true" auth="false"/>
        <event type="java" path="org.ofbiz.webapp.control.LoginWorker" invoke="checkLogin" />
        <response name="success" type="view" value="main"/>
        <response name="error" type="view" value="login"/>
    </request-map>

    <request-map uri="login">
        <security https="true" auth="false"/>
        <event type="java" path="org.ofbiz.webapp.control.LoginWorker" invoke="login"/>
        <response name="success" type="view" value="main"/>
        <response name="error" type="view" value="login"/>
    </request-map>

    <request-map uri="logout">
        <security https="true" auth="true"/>
        <event type="java" path="org.ofbiz.webapp.control.LoginWorker" invoke="logout"/>
        <response name="success" type="request" value="checkLogin"/>
        <response name="error" type="view" value="main"/>
    </request-map>

...

These requests are needed to add in your controller only when you have not included any of the other component controller which consist of these requests. So if you have already included common-controller.xml file then you don't need to explicitly do these entries in your controller.    
and the same view we have in place can be used for which we have entry in common-controller.xml file we can also have our own:

Code Block
<view-map name="login" type="screen" page="component://common/widget/CommonScreens.xml#login"/>

...

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. you request should look like :

Code Block
<request-map uri="main">
    <security https="true" auth="true"/>
    <response name="success" type="view" value="main"/>
    <response name="error" type="view" value="main"/>
</request-map>

...

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 :

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

...

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.

...

This new feature enables you to just define the services by mentioning the operation you want to perform.Basically just set the engine attribute to "entity-auto" and the invoke attribute to "create", "update", or "delete".
like you can take a look in the following code from services.xml of example component:  

Code Block
<service name="createExample" default-entity-name="Example" engine="entity-auto" invoke="create" auth="true">
    <description>Create a Example</description>
    <permission-service service-name="exampleGenericPermission" main-action="CREATE"/>
    <auto-attributes include="pk" mode="OUT" optional="false"/>
    <auto-attributes include="nonpk" mode="IN" optional="true"/>
    <override name="exampleTypeId" optional="false"/>
    <override name="statusId" optional="false"/>
    <override name="exampleName" optional="false"/>
</service>

...

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

Code Block
<request-map uri="createPracticePersonSimpleEvent">
    <security https="true" auth="true"/>{color}
    <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>

...

and for java event controller entry will be like:

Code Block
<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="{color}{color:#0033ff}CreatePracPersonJavaEvent{color}{color:#0033ff}"/>
    <response name="error" type="view" value="{color}{color:#0033ff}CreatePracPersonJavaEvent{color}{color:#0033ff}"/>
</request-map>

...

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

Code Block
<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>&nbsp;&nbsp;&nbsp;
        </process>
    </simple-map-processor>
</call-map-processor>
<check-errors/>

...

Step - 1 : The contents will 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;

...

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

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

...

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 :

Code Block
<\!-\- 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 :

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

...

 Create a group service by name "partyGroup" like :

Code Block
<\!-\- 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>

...

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 :

Code Block
<!-- 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>

...

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

...

Step - 1 :  For extending an entity you can do in this manner in the entitydef/entitymodel.xml file of your custom application.

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

...

Step - 2 : Now we are going to create the data for a user for this we have to prepare it in a specific order for a party like :

Code Block
<?xml version="1.0" encoding="UTF-8"?>
<entity-engine-xml>
    <Party partyId="DemoUser" partyTypeId="PERSON"/>
    <Person partyId="DemoUser" firstName="Practice" lastName="Person"/>
    <PartyRole partyId="DemoUser" roleTypeId="VISITOR"/>

    <ContactMech contactMechId="5000" contactMechTypeId="EMAIL_ADDRESS" infoString="practice.person@gmail.com"/>
    <PartyContactMech partyId="DemoUser" contactMechId="5000" fromDate="2001-05-13 00:00:00.000" allowSolicitation="Y"/>
    <PartyContactMechPurpose partyId="DemoUser" contactMechId="5000" contactMechPurposeTypeId="PRIMARY_EMAIL" fromDate="2001-05-13 00:00:00.000"/>
</entity-engine-xml>

...

Step - 3:  Now we have to an entry in ofbiz-component.xml file :

Code Block
<entity-resource type="data" reader-name="demo" loader="main" location="data/PracticeData.xml"/>

...