POJO Service Engine

From Opentaps Wiki
Jump to navigationJump to search

POJO Service Engine

The POJO service engine is designed to allow you to mount your Java service objects directly on to the ofbiz service engine, without having to write a static Java method to call it. To use the POJO service engine, you have to declare your service with service engine XML file, just like for all of the other ofbiz services, but use pojo instead of java as the engine:

   <service name="opentaps.invoiceNonPhysicalOrderItems" engine="pojo"
        location="org.opentaps.financials.domain.billing.invoice.OrderInvoicingService" invoke="invoiceNonPhysicalOrderItems">
        <description>Creates an invoice from the non-physical items on the order.  It will invoice from the status in the orderItemStatusId,
        or if it is not supplied, default to ITEM_PERFORMED.  After the invoice is created, it will attempt to change the items' status
        to ITEM_COMPLETE.</description>
        <attribute name="orderId" type="String" mode="IN" optional="false"/>
        <attribute name="orderItemStatusId" type="String" mode="IN" optional="true"/>
        <attribute name="invoiceId" type="String" mode="OUT" optional="false"/>

Then, you could write your service as a Java class with the following requirements:

  1. It must extend the base org.opentaps.foundation.service.ofbiz.Service class
  2. You must have a default constructor which takes no parameters
  3. For each input parameter defined in your services XML, you must have a set method. The set method must be named "set" plus the name of the variable, with the first letter capitalized. For example, if you have "orderId" as an input parameter of your service, you must have a "setOrderId" method. It can not be "SetOrderId", "setorderid", or "setorderId". This is done intentionally to enforce code consistency.
  4. Each set method must take one parameter, and it must match the parameter in your services XML. For example, if your services XML specifies "java.util.List", your set method take a single parameter of the java.util.List class, not ArrayList, FastList, LinkedList, etc.
  5. For each output parameter defined in your services XML, you must define one get method which takes no parameters. The name of the get method must be "get" plus the name of the variable with the first letter capitalized (ie, "getInvoiceId()" for "invoiceId").
  6. The invoke method must be a void method with no parameters.
  7. Instead of returning ServiceUtil.returnError when there is a problem with the service, throw exceptions such as ServiceException

You can define more than one service inside the same Java class by using different void methods without parameters. The same Java class should be shared among services that have similar input and output parameters.

Within the service, you can:

  • access other domains of the Domain Driven Architecture using the getDomainsDirectory() method of the Service superclass.
  • get the ofbiz framework's delegator and dispatcher through the Infrastructure with the getInfrastructure() method, then calling getDelegator() and getDispatcher()
  • get a UserLogin GenericValue for running legacy services written in the ofbiz framework by getting the User object from the getUser() method, then calling its getOfbizUserLogin() method

Here is a complete example:

public class OrderInvoicingService extends Service implements OrderInvoicingServiceInterface {

    private static final String module = OrderInvoicingService.class.getName();

    protected String orderId = null;
    protected String invoiceId = null;
    // by default, non-physical order items in this state will be invoiced
    protected String statusIdForNonPhysicalItemsToInvoice = OrderSpecification.ITEM_STATUS_PERFORMED;

    public OrderInvoicingService() {

    public OrderInvoicingService(Infrastructure infrastructure, User user, Locale locale) throws ServiceException {
        super(infrastructure, user, locale);

    public void setOrderId(String orderId) {
        this.orderId = orderId;

    public String getInvoiceId() {
        return this.invoiceId;

     * Set the status id of non-physical order items to be invoiced by invoiceNonPhysicalOrderItems, or
     * OrderSpecification.ITEM_STATUS_PERFORMED will be used
     * @param statusId
    public void setOrderItemStatusId(String statusId) {
        if (statusId != null) {
            statusIdForNonPhysicalItemsToInvoice = statusId;

    public void invoiceNonPhysicalOrderItems() throws ServiceException {
        try {
            // validate that the order actually exists and get list of non-physical
            OrderDomainInterface orderDomain = getDomainsDirectory().getOrderDomain();
            OrderRepositoryInterface orderRepository = orderDomain.getOrderRepository();
            Order order = orderRepository.getOrderById(orderId);
            List<OrderItem> itemsToInvoice = order.getNonPhysicalItemsForStatus(statusIdForNonPhysicalItemsToInvoice);

            // check if there are items to invoice
            if (UtilValidate.isEmpty(itemsToInvoice)) {
                throw new ServiceException("OpentapsError_PerformedItemsToInvoiceNotFound", UtilMisc.toMap("orderId", orderId));

            // create a new invoice for the order items
            // because of the way createInvoiceForOrder is written (665 lines of code!) we'd have to do some re-factoring before we can add the items to an existing invoice
            Map tmpResult = getInfrastructure().getDispatcher().runSync("createInvoiceForOrder", UtilMisc.toMap("orderId", orderId, "billItems", Repository.genericValueFromEntity(getInfrastructure().getDelegator(), "OrderItem", itemsToInvoice), "userLogin", getUser().getOfbizUserLogin()), 7200, false);  // no new transaction
            if (ServiceUtil.isError(tmpResult)) {
                throw new ServiceException(ServiceUtil.getErrorMessage(tmpResult));

            // change the status of the order items to COMPLETED
            order.setItemsStatus(itemsToInvoice, OrderSpecification.ITEM_STATUS_COMPLETED);

            // set the invoiceId of new invoice created
            this.invoiceId = (String) tmpResult.get("invoiceId");

        } catch (GeneralException ex) {
            throw new ServiceException(ex) ;