6 min read

Defining a Service

We first need to define a service. Our first service will be named learningFirstService.

In the folder ${component:learning}, create a new folder called servicedef. In that folder, create a new file called services.xml and enter into it this:

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

<services
xsi:noNamespaceSchemaLocation=”http://www.ofbiz.org/dtds/services.xsd”>
<description>Learning Component Services</description>

<service name=”learningFirstService” engine=”java”
location=”org.ofbiz.learning.learning.LearningServices” invoke=”learningFirstService”>
<description>Our First Service</description>
<attribute name=”firstName” type=”String” mode=”IN” optional=”true”/>
<attribute name=”lastName” type=”String” mode=”IN” optional=”true”/>
</service>
</services>

In the file ${component:learning}ofbiz-component.xml, add after the last <entity-resource> element this:

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

That tells our component learning to look for service definitions in the file ${component:learning}servicedefservices.xml.

It is important to note that all service definitions are loaded at startup; therefore any changes to any of the service definition files will require a restart!

Creating the Java Code for the Service

In the package org.ofbiz.learning.learning, create a new class called LearningServices with one static method learningFirstService:

package org.ofbiz.learning.learning;

import java.util.Map;

import org.ofbiz.service.DispatchContext;
import org.ofbiz.service.ServiceUtil;

public class LearningServices {

public static final String module = LearningServices.class.getName();

public static Map learningFirstService(DispatchContext dctx, Map context){

Map resultMap = ServiceUtil.returnSuccess(“You have called on service
‘learningFirstService’ successfully!”);
return resultMap;
}

}

Services must return a map. This map must contain at least one entry. This entry must have the key responseMessage (see org.ofbiz.service.ModelService.RESPONSE_MESSAGE), having a value of one of the following:

  • success or ModelService.RESPOND_SUCCESS
  • error or ModelService.RESPOND_ERROR
  • fail or ModelService.RESPOND_FAIL

By using ServiceUtil.returnSuccess() to construct the minimal return map, we do not need to bother adding the responseMessage key and value pair.

Another entry that is often used is that with the key successMessage (ModelService.SUCCESS_MESSAGE). By doing ServiceUtil.returnSuccess(“Some message”), we will get a return map with entry successMessage of value “Some message”. Again, ServiceUtil insulates us from having to learn the convention in key names.

Testing Our First Service

Stop OFBiz, recompile our learning component and restart OFBiz so that the modified ofbiz-component.xml and the new services.xml can be loaded.

In ${component:learning}widgetlearningLearningScreens.xml, insert a new Screen Widget:

<screen name="TestFirstService">
  <section>
    <widgets>
      <section>
        <condition><if-empty field-name="formTarget"/></condition>
        <actions>
        	<set field="formTarget" value="TestFirstService"/>
        	<set field="title" value="Testing Our First Service"/>
        </actions>
        <widgets/>
      </section>
      <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
        <decorator-section name="body">
          <include-form name="TestingServices" 
           location="component://learning/widget/learning/LearningForms.xml"/>
           <label text="Full Name: ${parameters.fullName}"/>
        </decorator-section>
      </decorator-screen>
    </widgets>
  </section>
</screen>

In the file ${component:learning}widgetlearningLearningForms.xml, insert a new Form Widget:

<form name="TestingServices" type="single" target="${formTarget}">
  <field name="firstName"><text/></field>
  <field name="lastName"><text/></field>
  <field name="planetId"><text/></field>
  <field name="submit"><submit/></field>
</form>

Notice how the formTarget field is being set in the screen and used in the form. For now don’t worry about the Full Name label we are setting from the screen. Our service will eventually set that.

In the file ${webapp:learning}WEB-INFcontroller.xml, insert a new request map:

<request-map uri="TestFirstService">
  <event type="service" invoke="learningFirstService"/>
  <response name="success" type="view" value="TestFirstService"/>
</request-map>

The control servlet currently has no way of knowing how to handle an event of type service, so in controller.xml we must add a new handler element immediately under the other <handler> elements:

<handler name="service" type="request" class="org.ofbiz.webapp.event.ServiceEventHandler"/>
<handler name="service-multi" type="request" class="org.ofbiz.webapp.event.ServiceMultiEventHandler"/>

We will cover service-multi services later. Finally add a new view map:

<view-map name="TestFirstService" type="screen" 
      page="component://learning/widget/learning/LearningScreens.xml#TestFirstService"/>

Fire to webapp learning an http OFBiz request TestFirstService, and see that we have successfully invoked our first service:

Apache OFBiz Service Engine: Part 1

Service Parameters

Just like Java methods, OFBiz services can have input and output parameters and just like Java methods, the parameter types must be declared.

Input Parameters (IN)

Our first service is defined with two parameters:

<attribute name="firstName" type="String" mode="IN" optional="true"/>
<attribute name="lastName" type="String" mode="IN" optional="true"/>

Any parameters sent to the service by the end-user as form parameters, but not in the services list of declared input parameters, will be dropped. Other parameters are converted to a Map by the framework and passed into our static method as the second parameter.

Add a new method handleInputParamaters to our LearningServices class.

public static Map handleParameters(DispatchContext dctx, Map context){

String firstName = (String)context.get(“firstName”);
String lastName = (String)context.get(“lastName”);
String planetId= (String)context.get(“planetId”);

String message = “firstName: ” + firstName + “<br/>”;
message = message + “lastName: ” + lastName + “<br/>”;
message = message + “planetId: ” + planetId;

Map resultMap = ServiceUtil.returnSuccess(message);
return resultMap;
}

We can now make our service definition invoke this method instead of the learningFirstService method by opening our services.xml file and replacing:

<service name="learningFirstService" engine="java" 
  location="org.ofbiz.learning.learning.LearningServices" invoke="learningFirstService">

with:

<service name="learningFirstService" engine="java" 
  location="org.ofbiz.learning.learning.LearningServices" invoke="handleParameters">

Once again shutdown, recompile, and restart OFBiz.

Enter for fields First Name, Last Name, and Planet Id values Some, Name, and Earth, respectively. Submit and notice that only the first two parameters went through to the service. Parameter planetId was dropped silently as it was not declared in the service definition.

Apache OFBiz Service Engine: Part 1

Modify the service learningFirstService in the file ${component:learning}servicedefservices.xml, and add below the second parameter a third one like this:

<attribute name="planetId" type="String" mode="IN" optional="true"/>

Restart OFBiz and submit the same values for the three form fields, and see all three parameters go through to the service.

Apache OFBiz Service Engine: Part 1

 

LEAVE A REPLY

Please enter your comment!
Please enter your name here