Difference between revisions of "Writing Object Oriented Code in OFBiz Framework"

From Opentaps Wiki
Jump to navigationJump to search
(New page: Although the ofbiz framework is itself not object-oriented, you can write object-oriented code which takes advantage of all the components in existing ofbiz applications. This would allow...)
 
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
Although the ofbiz framework is itself not object-oriented, you can write object-oriented code which takes advantage of all the components in existing ofbiz applications.  This would allow you to reuse its features while also allowing you to write nice, clean object-oriented code that can be redeployed to another framework, such as Spring, or serialized to client side applications, or generally reused in your organization without having to go through the ofbiz framework in some fashion.
+
'''This document is deprecated.  See [[Domain Driven Architecture]] instead.'''
 +
 
 +
Although the ofbiz framework is itself not object-oriented, you can write object-oriented code which takes advantage of all the components in existing ofbiz applications.  This would allow you to reuse its features while also allowing you to write clean object-oriented code that is more maintainable, can be redeployed to another framework, such as Spring, serialized to client side applications, or generally reused in your organization without having to go through the ofbiz framework in some fashion.
  
 
===Writing Object Oriented Code===
 
===Writing Object Oriented Code===
Line 105: Line 107:
 
</pre>
 
</pre>
  
Then, in your service, simply move out the bulk of your code into an Object, and
+
Then, in your service, simply move out the bulk of your code into an Object, and change your service to a call to the object.  You can pass the context of your Java service directly into your new object, like this:
 +
 
 +
<pre>
 +
  public static Map appendOrderItemBasic(DispatchContext dctx, Map context) {
 +
      LocalDispatcher dispatcher = dctx.getDispatcher();
 +
 +
      // move your original service layer code to your object, and substitute with just this:
 +
      try {
 +
        MyObject myObject = new MyObject(context, dispatcher);
 +
        myObject.doThis();
 +
        myObject.doThat();
 +
      } catch (MyObjectException1 ex) {
 +
            . . .   
 +
  }
 +
</pre>
 +
 
 +
===Working with Security=== 
 +
 
 +
Moving to an object oriented framework can greatly increase your security options, because you can now write your security operations as a separate plugin class.  Simply pass the userLogin GenericValue as one of the parameters to your object's constructor, and then create a new security object to work with it.  You also have the advantage of throwing multiple types of exceptions to handle different security issues.  For example, you can write your constructor like this:
 +
<pre>
 +
 
 +
public MyObject(String param1, BigDecimal param2, GenericValue userLogin, Security security, LocalDispatcher dispatcher) {
 +
      if (security == null) {
 +
          Security security = new Security(...);
 +
      }
 +
 +
      // check security first.  Note that in this case we're illustrating the ofbiz security object, although you can certainly write a security wrapper
 +
      // and use your own
 +
      if (security.hasEntityPermission("MODULE", "OPERATION", userLogin) {
 +
          // do various interesting things
 +
      } else {
 +
          throw MyObjectSecurityException1 ...;
 +
      }
 +
  }
 +
 
 +
</pre>
 +
 
 +
You can then have methods that plugin various other security and user login objects:
 +
 
 +
<pre>
 +
// this method uses default security
 +
public MyObject(String param1, BigDecimal param2, GenericValue userLogin, LocalDispatcher dispatcher) {
 +
    return MyObject(param1, param2, userLogin, new MySecurityObject(...), dispatcher);
 +
}
 +
 
 +
// this method uses a custom login object, which could be an extension of the standard GenericValue or offer a method .toGenericValue
 +
// to convert to an ofbiz GenericValue
 +
public MyObject(String param1, BigDecimal param2, MyLoginObject userLogin, LocalDispatcher dispatcher) {
 +
    return MyObject(param1, param2, userLogin, new MySecurityObject(...), dispatcher);
 +
}
 +
</pre>
 +
 
 +
Again, this illustration shows the ofbiz userLogin and security objects as core and your userlogin and security as extensions, but it doesn't have to be that way.  You can make the main constructor of your method use your userLogin and security objects and ofbiz constructors convert to it.

Latest revision as of 01:04, 26 June 2008

This document is deprecated. See Domain Driven Architecture instead.

Although the ofbiz framework is itself not object-oriented, you can write object-oriented code which takes advantage of all the components in existing ofbiz applications. This would allow you to reuse its features while also allowing you to write clean object-oriented code that is more maintainable, can be redeployed to another framework, such as Spring, serialized to client side applications, or generally reused in your organization without having to go through the ofbiz framework in some fashion.

Writing Object Oriented Code

The ofbiz framework is basically a model-view-controller framework whose model portion is (usually) in a service layer. Controller requests call services directly. Data is passed between various layers as Maps. To make it more object oriented, you can create an object layer which is referenced via either servlets or services from your controller, and those objects can in turn use the ofbiz services and delegator as necessary.

For example, a request to the controller is usually routed to a service call:

    <request-map uri="myRequest">
        <security https="true" auth="true"/>
        <event type="service" invoke="myService"/>
        <response name="success" type="request-redirect" value="myViewPage"/>
        <response name="error" type="view" value="myErrorPage"/>
    </request-map>

To make it more object oriented, there are two approaches: you can either use a servlet in place of a service in your controller:

    <request-map uri="myRequest">
        <security https="true" auth="true"/>
        <event type="java" path="org.opentaps.MyServlet" invoke="myServletMethod"/>
        <response name="success" type="request-redirect" value="myViewPage"/>
        <response name="error" type="view" value="myErrorPage"/>
    </request-map>

Then, your servlet method can in turn take the parameters and instantiate an object. To make this easier, we can use the UtilHttp.getParameterMap(request) method to create a Map which matches a Map-based constructor in your object. The Map-based constructor can also be good for backward compatbility with other ofbiz-based methods. For example, your servlet can do this:


    public static String myServletMethod(HttpServletRequest request, HttpServletResponse response) {
         // the service dispatcher is used to access the ofbiz services and business logic.  Note that it also has a delegator so you don't need both.
         LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");

         try {
             MyObject myObject = new MyObject(UtilHttp.getParamterMap(request), dispatcher);
             myObject.doThis();
             myObject.doThat();
         } catch (MyObjectException1 ex) {
             . . . 
         } catch (MyObjectException2 ex) {
             . . .
         } catch (MyObjectException3 ex) {
             . . . 
         } catch (MyObjectException4 ex) {
             . . .
         }
    }

One of the key advantages of this approach is that it allows multiple exceptions to be thrown and caught separately, rather than relying on the ofbiz service engine, which only allows returning success, error (which causes a rollback), and failure (which does not.)

Your object can be just like a regular Java object (POJO), except that it should interact with the ofbiz framework's Map-based parameters, service dispatcher, and database delegator. A sample setup might be like this:


public class MyObject {
   public static final GenericDelegator delegator = null;
   public static final LocalDispatcher dispatcher = null;

   // this method is basically for ofbiz compatibility and convert the Map of parameters for the regular constructor 
   public MyObject(Map context, LocalDispatcher dispatcher) {
       return MyObject(context.get("param1"), context.get("param2"), dispatcher);
   }

   // this is your main constructor, which can be invoked from outside of the ofbiz framework as well
   // we recommend that, if possible, you do all your data-gathering in this method so your object can stand on its own and be serialized
   public MyObject(String param1, BigDecimal param2, LocalDispatcher dispatcher) {
       // do various interesting things
   }

   // this method is used to access the service layer and call various ofbiz services.  Note again how the exception handling is more flexible.
   public static void updateMyObject() {
       try {
           Map tmpResult = dispatcher.runSync("service", UtilMisc.toMap(....));

           if ServiceUtil.isError(tmpResult) { 
               throw MyObjectException1(...);
           } else if (ServiceUtil.isFailure(tmpResult)) {
               throw MyObjectException2(...);
           }
       } catch (GenericEntityException ex) {
          throw MyObjectException3(...); 
       } catch (GenericServiceException ex) {
          throw MyObjectException4(...); 
       }
       
   }

If you need to, of course, you can follow an Inversion of Control (IOC) pattern and segregate the methods for constructing your object and doing the updates into a separate class, so that you can substitute the ofbiz dispatcher/delegator based methods later for Hibernate/Spring based ones.

Working with Legacy Ofbiz Code

If you have a lot of ofbiz service-engine-driven code already, you can migrate it to an object-oriented approach fairly easily (provided it's written in an object oriented language like Java, rather than minilang). Simply replace your old services with calls to your new object, and you can keep the controller as is from before. For example, your controller might say:

    <request-map uri="myRequest">
        <security https="true" auth="true"/>
        <event type="service" invoke="myService"/>
        <response name="success" type="request-redirect" value="myViewPage"/>
        <response name="error" type="view" value="myErrorPage"/>
    </request-map>

Then, in your service, simply move out the bulk of your code into an Object, and change your service to a call to the object. You can pass the context of your Java service directly into your new object, like this:

   public static Map appendOrderItemBasic(DispatchContext dctx, Map context) {
      LocalDispatcher dispatcher = dctx.getDispatcher();
 
      // move your original service layer code to your object, and substitute with just this:
      try {
         MyObject myObject = new MyObject(context, dispatcher);
         myObject.doThis();
         myObject.doThat();
      } catch (MyObjectException1 ex) {
             . . .     
   }

Working with Security

Moving to an object oriented framework can greatly increase your security options, because you can now write your security operations as a separate plugin class. Simply pass the userLogin GenericValue as one of the parameters to your object's constructor, and then create a new security object to work with it. You also have the advantage of throwing multiple types of exceptions to handle different security issues. For example, you can write your constructor like this:


 public MyObject(String param1, BigDecimal param2, GenericValue userLogin, Security security, LocalDispatcher dispatcher) {
      if (security == null) {
          Security security = new Security(...);
      }
 
      // check security first.  Note that in this case we're illustrating the ofbiz security object, although you can certainly write a security wrapper
      // and use your own
      if (security.hasEntityPermission("MODULE", "OPERATION", userLogin) { 
          // do various interesting things 
      } else {
          throw MyObjectSecurityException1 ...;
      }
   }

You can then have methods that plugin various other security and user login objects:

 // this method uses default security
 public MyObject(String param1, BigDecimal param2, GenericValue userLogin, LocalDispatcher dispatcher) {
    return MyObject(param1, param2, userLogin, new MySecurityObject(...), dispatcher); 
 }

 // this method uses a custom login object, which could be an extension of the standard GenericValue or offer a method .toGenericValue 
 // to convert to an ofbiz GenericValue
 public MyObject(String param1, BigDecimal param2, MyLoginObject userLogin, LocalDispatcher dispatcher) {
    return MyObject(param1, param2, userLogin, new MySecurityObject(...), dispatcher); 
 }

Again, this illustration shows the ofbiz userLogin and security objects as core and your userlogin and security as extensions, but it doesn't have to be that way. You can make the main constructor of your method use your userLogin and security objects and ofbiz constructors convert to it.