Java Classes for Constants

From Opentaps Wiki
Jump to navigationJump to search

String contants are the achilles' heel of software: You can fortify your code with object oriented design and annotations, use the latest IDE tools, and still fall victim to:

if ("ORDER_APPPROVED".equals(order.getStatusId()))

This bug will stay hidden from all your compile tools, and you won't find it until you (or your user) tried to use this feature. And then ... without any tools designed to find bugs like this, how long will it take you to fix it?

In opentaps 1.4 (post preview 2), we can now generate Java classes based on the constants from your database. This can be very useful to check for types or statuses on which your business logic rely. Your IDE or Java compiler will flag the wrong constant ID values, and you will know if the constants have been changed or removed by somebody else.

To use the Java constants, start with the org.opentaps.base.constants package, and your IDE should guide you to the right class, subclass, and constant value, such as in Eclipse:

Eclipse using java constants.png

The configuration located in


defines which entities values to make into Java constants. To generate the constants, make sure the data is loaded in the database and run

ant make-constants

Configuration: Flat Structures

For flat structures, only the entity name and the field containing the constant value are needed. For example we export the OrderTypes like this:

  • generate constants for the OrderType entity
   OrderType = generate
  • the values are in the orderTypeId field
   OrderType.constantField = orderTypeId
  • use the description field in the Javadoc for each value
   OrderType.descriptionField = description

The end result is the file

    * OrderType constant values.
   public final class OrderTypeConstants {
       private OrderTypeConstants() { }
       /** Purchase. */
       public static final String PURCHASE_ORDER = "PURCHASE_ORDER";
       /** Sales. */
       public static final String SALES_ORDER = "SALES_ORDER";

Configuration: Categorized structure

For type based structures, you can create inner classes for each the type of constant values. typeField is used to indicate the field specifying the type of each value. For example we the StatusItems contains a list a statuses for orders, invoices, order items, etc ... Its configuration is:

   StatusItem = generate
   StatusItem.typeField = statusTypeId
   StatusItem.constantField = statusId
   StatusItem.descriptionField = description

The end result is the file:


For each type an inner class is generated containing its constant values.

    * StatusItem constant values.
   public final class StatusItemConstants {
       private StatusItemConstants() { }  
       public static final class OrderStatus {
           private OrderStatus() { }
           /** Created. */
           public static final String ORDER_CREATED = "ORDER_CREATED";
           /** Processing. */
           public static final String ORDER_PROCESSING = "ORDER_PROCESSING";
           /** Approved. */
           public static final String ORDER_APPROVED = "ORDER_APPROVED";
           /** Sent. */
           public static final String ORDER_SENT = "ORDER_SENT";
           /** Held. */
           public static final String ORDER_HOLD = "ORDER_HOLD";
           /** Completed. */
           public static final String ORDER_COMPLETED = "ORDER_COMPLETED";
           /** Rejected. */
           public static final String ORDER_REJECTED = "ORDER_REJECTED";
           /** Cancelled. */
           public static final String ORDER_CANCELLED = "ORDER_CANCELLED";
           /** Undeliverable. */
           public static final String ORDER_UNDELIVERABLE = "ORDER_UNDELIVERABLE";

Configuration: Tree structures

Finally, some data are structured like a tree. This is partially supported by using the same approach as above.

For example ProductTypes has simple parent / child relationship:

  • GOOD
    • [...]
    • [...]

where SUPPLIES has no children whereas GOOD and SERVICE have some.

This is configured just like categorized constants:

   PaymentType = generate
   PaymentType.typeField = parentTypeId
   PaymentType.constantField = paymentTypeId
   PaymentType.descriptionField = description

The end result is the file:

    * ProductType constant values.
   public final class ProductTypeConstants {
       private ProductTypeConstants() { }
       public static final class Good {
           private Good() { }
           /** Good. */
           public static final String GOOD = "GOOD";
           /** Raw Material. */
           public static final String RAW_MATERIAL = "RAW_MATERIAL";
           /** Subassembly. */
           public static final String SUBASSEMBLY = "SUBASSEMBLY";
           /** Finished Good. */
           public static final String FINISHED_GOOD = "FINISHED_GOOD";
       public static final class Service {
           private Service() { }
           /** Service. */
           public static final String SERVICE = "SERVICE";
           /** Contracted Manufacturing Service. */
           public static final String SERVICE_CONTRACT_MFG = "SERVICE_CONTRACT_MFG";
       /** Supplies. */
       public static final String SUPPLIES = "SUPPLIES";

For each type an inner class is generated containing its constant values, and the parent is also moved to the inner class. (The general rule is that: if there is an entity A where typeField is null and constantField equals the type field of another entity B, we consider A.typeField = A.constantField).

This does not work for deeper than one level for now. Data using deeper tree structures are easier to deal with as a flat constant file anyway as it get more difficult to find the constants.

Changes in 1.4 preview 3

Note that from opentaps 1.4 preview 3 and later, the java package has been changed from org.opentaps.domain.base.constants to org.opentaps.base.constants.