Versions Compared

Key

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

...

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 - 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/PracticeScreensCommonScreens.xml" in for the location and you will see why in a while.

...

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.

...

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

...

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

The above statement will fetch all the records from the Person entity and will put them in context by the name persons. Now this list by the name person will be iterated in the ftl file to show the records.
At this place an important reading is available at : Which variables are available in screen context?

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>

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.

...

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

That 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

...

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

...

Now run your application and observe the difference. you can login by user name : admin and pwd: ofbizHere we should understand why we had given the permission "OFBTOOLS" in base-permission in ofbiz-component.xml file. To understand this please read following carefully and perform steps as mentioned:

...

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>

engine="entity-auto" invoke="create" play the role for creating the records for the default-entity "Example."
Here for practice you may go by following further steps those steps will help you in understanding the concept then onwards you can practice the pattern given above  in your code only.

...

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>

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:

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>

Here the path is the classpath in which this event is defined. 
The file name will be PracticeEvents.java and will be created at  practice/src/org/hotwax/practice.

...

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

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.

...

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;

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.

...

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

...

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

 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.

...

 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>

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.

...

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.

...

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>

As an example of this you can refer entitymodel.xml file from party component.
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.

...

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>

The purpose is to create a record for a party who is a person with a role of VISITOR and creating an email address which is a primary email address for that party.
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"/>

...