Exercise 2: EMF Programming
Scenario
You will be building tools to work with purchase orders. Your company has existing shipping and billing systems that
you need to integrate with. The basis for this integration will be an XML Schema for purchase orders,
po.xsd, which is already used by the shipping system.
In this exercise, you'll implement a purchase order filling action that will be added to the generated editor. You
will write code that processes purchase order data using the EMF runtime APIs, the APIs generated from the model, and
the APIs for the existing shipping and billing systems.
Objectives
At the end of the exercise you should be able to:
- Use the persistence framework to serialize EMF data
- Use generated model APIs to examine and update objects
- Validate objects against constraints defined in the model
- Use a change recorder to provide a simple roll-back mechanism
Required Software
- Java 5.0 or later
- Eclipse 3.3 or later
- EMF 2.3 or later
The screen captures in this document are based on Eclipse 3.4 M5 and EMF 2.4 M5.
Directions
This exercise is carried out entirely using the Eclipse Software Development Kit (SDK) with the Eclipse Modeling
Framework (EMF) SDK installed into it. These instructions refer to this product as either Eclipse or the
workbench.
This exercise builds on the three projects generated in Exercise 1. You will be working with two additional
projects:
- com.example.po.processing provides the existing shipping and billing APIs that you will use in
your code. There are two classes of interest: Shipper and Biller. Both
include Javadoc that describes exactly how to use them.
- com.example.po.processing.ui provides the purchase order filling action that you will
implement. The UI contribution is already provided.
The full solution to this exercise is available in the Solutions folder. That folder contains the
completed com.example.po.processing.ui project.
Part 1: Run the Fill Order Action
A stub FillOrderAction is provided in the com.example.po.processing.ui
project. Although not fully implemented, it can already be run from the UI.
- Launch a new Eclipse workbench in debug mode.
- In the Package Explorer view, select the com.example.po.processing.ui
project.
- Select Eclipse Application from the Debug toolbar drop-down. It's
worth running in debug mode in this exercise, as it can prove helpful when you're testing and debugging the
code you'll write.
- In the newly launched Eclipse workbench, open the instance document, supplier.po, created
in Exercise 1.
- There is a sample document, _EMF_Tutorial/supplier.po, which you can copy from the first
workspace, if you wish.
- Right-click the file and select Open With -> PO Model Editor from the pop-up menu.
The supplier is opened in the generated editor.
- Run the Fill Order action.
- Expand the Document Root and Supplier to see the list of orders.
- Select one of the Purchase Orders and choose Fill Order from the
PO Editor menu. Note that this action will only be enabled if a single purchase order is
selected and if that order has at least one item with no ship date set.

- A dialog shows that the order was not successfully filled. Click the Details button, and
you'll see that the action has not yet been implemented.
When implemented, the Fill Order action will set the ship date of items as they are shipped. So,
it considers an order already filled if this date is set on all of its items. If you are unable to run the action
because of this, you can clear the ship date on items by selecting Ship Date in the
Properties view and clicking on its Restore Default Value toolbar button.
Or, you can just use the sample supplier.po file, which has no ship dates set initially.

Part 2: Implement the Fill Order Action
In the remainder of this exercise, you will implement the Fill Order action. You'll do your
development in the first workbench and launch a second workbench in the debugger whenever you need to test it.
In the com.example.po.processing.ui project, expand the packages under the src
folder and open FillOrderAction.java. Find the fillOrder() method. As you'll
see, it's just a stub that throws an exception. Implement the method as follows:
- Create a new resource set, and a resource in it to use in serializing the purchase order. Make a deep copy of the
selected purchase order, and use that copy in a new document root as the resource's contents.
- You won't actually serialize the order to the resource's URI, but directly to a string. Thus, the URI you use
to create the resource doesn't matter at all, except that it must end with a .po
extension, so that the correct resource factory is used. For example, you could simply create it like this:
URI.createURI(".po")
- Save the resource to a StringWriter.
- You should use the two-argument form of Resource.save() to specify an
OutputStream directly, instead of letting the resource obtain one from its URI. You can
wrap the StringWriter in a URIConverter.WritableOutputStream and avoid
the need to do any encoding/decoding, like this:
new URIConverter.WriteableOutputStream(writer, "UTF-8")
- Create a Shipper (recall that this a class from the
com.example.po.processing plug-in) and prepare the order.
- If any unshipped items are ready to be shipped, accumulate a list of those items, calculate their total cost, and
set their ship dates.
- The type of Item.getPrice() is BigDecimal. You should use this type to
do your calculations directly, as converting to float can introduce error in decimal
values.
- Call getCurrentDate() to obtain the current date as an instance of
XMLGregorianCalendar.
- Submit the credit card information and total cost to the Biller (also from
com.example.po.processing).
- Biller's integers for identifying a credit card type are the same as the
CardType enum's values, and its expiry date format matches the ordinary lexical
representation for XMLGregorianCalendar.
- If the billing transaction is successful, ship the order and return the shipped items.
- Otherwise, if payment is refused, cancel the shipment and return null.
- Otherwise, if no items are ready, just return an empty list.
Shipper and Biller log messages to standard out, which appear in the
Console view of the development workbench. Once the fillOrder() method is
complete, the action should report results that match these messages.
However, there are still couple of improvements left to be made, which you'll do in the last two parts of the
exercise.
Part 3: Add Validation
If you try to fill an invalid order (for example, with no order ID or no billing address), you'll see that the
shipper refuses the order by throwing a Shipper.InvalidPurchaseOrderException from
prepare(). This results in the display of the following dialog.

Clicking the Details button shows only the exception itself, which provides no detailed
information about what is wrong with the order. It would be better to perform validation right in the action. Then,
if any problems are found, the details of those problems can be shown to the user.
Add validation of the purchase order to the fillOrder() method as follows:
- After the selected purchase order has been copied and added to the resource, but before the resource is saved,
invoke validation on the copy of the purchase order.
- Use an instance of the inner class LabelProviderDiagnostician as the diagnostician. It
uses EMF.Edit item providers to display the same labels for objects as the editor, which is nicer than the
default behaviour of Diagnostician.
- If the resulting Diagnostic indicates an error, wrap it in a
DiagnosticException and throw it.
The diagnostic is then extracted from the exception and used to describe the problems in the dialog. Notice that the
resulting dialog looks just like the one displayed after running the Validate action in Exercise
1.
Part 4: Add Roll-back
After finding out from the shipper that items are available to be shipped, fillOrder() goes
through those items and sets the ship date on each. Unfortunately, there is a problem with this approach: if the
biller refuses payment, the items won't be shipped, but will already have had their ship dates set. You can solve
this problem by recording the changes and rolling them back if billing is unsuccessful.
Add change recording and roll-back to the fillOrder() method as follows:
- After the shipper prepares the order, use a ChangeRecorder to record changes made to the
purchase order while processing the items.
- End recording and obtain a ChangeDescription after processing the items.
- The change recorder is implemented as an adapter on all of the objects it is monitoring. After you have
obtained the change description, you should call dispose() on the change recorder to
detach it.
- If billing is unsuccessful, apply() the changes to roll back the transaction.
Summary
You implemented an action that fills purchase orders, using generated model APIs and the persistence framework to
manipulate and serialize EMF data. You then added validation to report any problems before attempting to process an
order, and implemented roll-back to leave the data unchanged in case of an incomplete transaction.