Customizing opentaps 2: an OSGi Tutorial

From Opentaps Wiki
Revision as of 00:48, 24 February 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 class in org/opentaps/notes/domain/ in modules/notes/api/services/src/main/java:

    private String clientDomain;

Notice that's all I'm adding -- a field (plus getters and setters with Eclipse). It doesn't matter how the field is actually implemented here, since this class is just an object-oriented representation of my data model.

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.

Now I need to change my implementation of the repository so that this field shows up in my database table and will be persisted correctly. To add this field to the database, I need to modify the JPA definition for, which is the org/opentaps/notes/entity/ class modules/notes/impl/repository/. Here I will add a new field (notice the JPA markup):

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

and use Eclipse to create get and set methods for it.

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.

To handle persistence of the field with the repository, I modify the class in modules/notes/impl/repository/ so that the clientDomain field is transferred between my abstract domain class and my persistence class

private static Note makeNote(NoteData noteData) {
   // ...
   // ...

private static NoteData makeNoteData(Note note) {
   // ...
   // ...

Now that my repository supports the new clientDomain field, I need to add support for it in my 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: