Customizing opentaps 2: an OSGi Tutorial

From Opentaps Wiki
Revision as of 22:30, 5 March 2012 by Sichen (talk | contribs)
Jump to navigationJump to search

In this tutorial, I will take you through the steps to customizing the opentaps 2 Notes application and show you how to work with the OSGi framework.

Right now our notes application is looking pretty good -- I can create notes from my html client application using the REST interface. I plan on using my notes application for comments from my different sites. To keep track of which site the note came from, I'd like to add a new field to store where the notes are coming from.

The first step is to add the field to my data model. Our data model is part of the repository from the Domain Driven Architecture, so there is an abstract representation of the data model in the repository interfaces and its concrete implementation with JPA for persistence with a database. We first need to add our new data field to the definition of notes, which is the domain class (see How to Create a new OSGi Bundle for more about this class):

    @Column(name = "CLIENT_DOMAIN")
    private String clientDomain;

I am adding the persistence markups as well, since may be used later for persistence.

I don't need to modify the repository interface, because the repository interface simply defines methods that operate on the class, and none of them are affected by this new field. If I were to need a method that worked on this field, such as returning a list of notes from a particular domain, however, I would need to add it to in the modules/notes/api module.

Just to make sure I did it correctly, I build opentaps 2 again with mvn clean install, use the Geronimo to uninstall the old org.opentaps.notes.eba and deploy the new one. Then I create another note from my html client and check the database:

mysql> show fields from NOTE_DATA;
| Field              | Type         | Null | Key | Default | Extra |
| NOTE_ID            | varchar(32)  | NO   | PRI | NULL    |       |
| ATTRIBUTE_1        | varchar(255) | YES  |     | NULL    |       |
| ATTRIBUTE_10       | varchar(255) | YES  |     | NULL    |       |
| ATTRIBUTE_2        | varchar(255) | YES  |     | NULL    |       |
| ATTRIBUTE_3        | varchar(255) | YES  |     | NULL    |       |
| ATTRIBUTE_4        | varchar(255) | YES  |     | NULL    |       |
| ATTRIBUTE_5        | varchar(255) | YES  |     | NULL    |       |
| ATTRIBUTE_6        | varchar(255) | YES  |     | NULL    |       |
| ATTRIBUTE_7        | varchar(255) | YES  |     | NULL    |       |
| ATTRIBUTE_8        | varchar(255) | YES  |     | NULL    |       |
| ATTRIBUTE_9        | varchar(255) | YES  |     | NULL    |       |
| CREATED_BY_USER_ID | varchar(255) | YES  |     | NULL    |       |
| DATE_TIME_CREATED  | datetime     | YES  |     | NULL    |       |
| USER_ID_TYPE       | varchar(255) | YES  |     | NULL    |       |
| SEQUENCE_NUM       | bigint(20)   | YES  |     | NULL    |       |
| NOTE_TEXT          | text         | YES  |     | NULL    |       |
| CLIENT_DOMAIN      | varchar(255) | YES  |     | NULL    |       |
17 rows in set (0.05 sec)

Yay! My new field was added automatically when I accessed the notes application again.

I don't need to make any changes to the repository's class in modules/notes/impl/repository/, since it simply operates on So the next step is to add support for the new field in the service layer.

Like the repository, there is an interface definition of my services and an implementation of those services. The interface is defined in modules/notes/api. There are separate classes for the service, its inputs, and its outputs. Since these are just interfaces, the service classes such as do nothing except say that you can:

    public CreateNoteServiceOutput createNote(CreateNoteServiceInput input) throws ServiceException;

so it doesn't have to be modified. We do need to add the new field to the input class,

    private String clientDomain;

plus its related get and set methods.

The implementation of the service needs to be modified so that it actually takes the clientDomain field from the service input and puts it into the class, so I modify the in modules/notes/impl/services/:

  public CreateNoteServiceOutput createNote(CreateNoteServiceInput input) throws ServiceException {
  // ...
  // ..

Now it is all done -- my domain data model has a new field clientDomain, which my repository and services will dutifully persist.

But where do I get this value from? I'll need to get it from the client application, which interacts with my server side through the REST interface. In modules/notes/rest, you will find with a method:

  public Representation createNote(Representation entity) throws NamingException, ServiceException  

This method will take a REST representation and interact with our service for creating the note. I add the new field here:


I think that's everything. I mvn clean install and say a silent prayer, promising only to write good code from now. Then I go to Geronimo to uninstall the old eba and deploy the new one. (Isn't it cool you don't have to stop and start the server any more?)

Finally, I use my client app again, type a new note, and check MySQL. Drumroll...

mysql> select note_id, note_text, client_domain from NOTE_DATA where date_time_created > '2012-02-23 16:45' order by sequence_num;
| note_id                          | note_text                             | client_domain |
| 28FD13DF0F414E22BB7A85BE966227EF | will you please save my client domain | localhost     |
1 row in set (0.00 sec)