Difference between revisions of "Java Classes for Constants"
m (→Changes from 1.4 preview 2) |
|||
Line 2: | Line 2: | ||
if ("ORDER_APPPROVED".equals(order.getStatusId())) | if ("ORDER_APPPROVED".equals(order.getStatusId())) | ||
− | This bug will stay hidden from all your compile tools, and you wont' find it until you (or your user) | + | This bug will stay hidden from all your compile tools, and you wont' 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. | 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. |
Revision as of 16:46, 2 April 2010
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 wont' 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.
The configuration located in
hot-deploy/opentaps-common/config/constantsGenerationConfig.properties
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
Contents
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
hot-deploy/opentaps-common/src/common/org/opentaps/base/constants/OrderTypeConstants.java
/** * 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:
hot-deploy/opentaps-common/src/common/org/opentaps/base/constants/StatusItemConstants.java
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:
- SUPPLIES
- GOOD
- RAW_MATERIAL
- SUBASSEMBLY
- FINISHED_GOOD
- [...]
- SERVICE
- SERVICE_CONTRACT_MFG
- [...]
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:
hot-deploy/opentaps-common/src/common/org/opentaps/base/constants/ProductTypeConstants.java
/** * 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.