Difference between revisions of "Java Classes for Constants"
(New page: Opentaps can now generate Java constants from the database values. This can be very useful to check for types or statuses on which the business logic can rely. The configuration located i...) |
|||
(5 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | + | String contants are the [http://en.wikipedia.org/wiki/Achilles%27_heel 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 <tt>org.opentaps.base.constants</tt> package, and your IDE should guide you to the right class, subclass, and constant value, such as in Eclipse: | ||
+ | |||
+ | [[Image:Eclipse_using_java_constants.png]] | ||
The configuration located in <pre>hot-deploy/opentaps-common/config/constantsGenerationConfig.properties</pre> defines which entities values to make into Java constants. | The configuration located in <pre>hot-deploy/opentaps-common/config/constantsGenerationConfig.properties</pre> defines which entities values to make into Java constants. | ||
To generate the constants, make sure the data is loaded in the database and run <pre>ant make-constants</pre> | To generate the constants, make sure the data is loaded in the database and run <pre>ant make-constants</pre> | ||
− | |||
== Configuration: Flat Structures == | == Configuration: Flat Structures == | ||
Line 17: | Line 25: | ||
OrderType.descriptionField = description | OrderType.descriptionField = description | ||
− | The end result is the file <pre>hot-deploy/opentaps-common/src/common/org/opentaps | + | The end result is the file <pre>hot-deploy/opentaps-common/src/common/org/opentaps/base/constants/OrderTypeConstants.java</pre> |
/** | /** | ||
Line 35: | Line 43: | ||
== Configuration: Categorized structure == | == Configuration: Categorized structure == | ||
− | For type based structures, the ''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: | + | 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 = generate | ||
Line 42: | Line 50: | ||
StatusItem.descriptionField = description | StatusItem.descriptionField = description | ||
− | The end result is the file: <pre>hot-deploy/opentaps-common/src/common/org/opentaps | + | The end result is the file: <pre>hot-deploy/opentaps-common/src/common/org/opentaps/base/constants/StatusItemConstants.java</pre> |
For each type an inner class is generated containing its constant values. | For each type an inner class is generated containing its constant values. | ||
Line 76: | Line 84: | ||
} | } | ||
− | == Configuration: Tree structures | + | == Configuration: Tree structures == |
Finally, some data are structured like a tree. This is partially supported by using the same approach as above. | Finally, some data are structured like a tree. This is partially supported by using the same approach as above. | ||
Line 100: | Line 108: | ||
PaymentType.descriptionField = description | PaymentType.descriptionField = description | ||
− | The end result is the file: <pre>hot-deploy/opentaps-common/src/common/org/opentaps | + | The end result is the file: <pre>hot-deploy/opentaps-common/src/common/org/opentaps/base/constants/ProductTypeConstants.java</pre> |
/** | /** | ||
Line 134: | Line 142: | ||
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. | 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 <tt>org.opentaps.domain.base.constants</tt> to <tt>org.opentaps.base.constants</tt>. |
Latest revision as of 18:49, 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 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:
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.