Enhance the Console Application

In this section we will enhance the console application to create and read timecards using the TimeTrackingService. We will need to add custom code to the business and data access layers to make the application work.

  1. Download the enhanced console application from here and replace the existing copy of TimeTrackerConsole.java at C:/timetracker/console/src/java/org/andromda/timetracker/console.
  2. Review the main() method in TimeTrackerConsole.java.
    public
    static
    void
    main(String[] args) {
    // Get services
    serviceLocator = ServiceLocator.instance(); peopleService = serviceLocator.getPeopleService(); timeTrackingService = serviceLocator.getTimeTrackingService();
    // Create people
    PersonVO naresh = createPerson(
    "nbhatia"
    ,
    "Naresh"
    ,
    "Bhatia"
    ); PersonVO louis = createPerson(
    "lcoude"
    ,
    "Louis"
    ,
    "Coude"
    ); PersonVO john = createPerson(
    "jsmith"
    ,
    "John"
    ,
    "Smith"
    );
    // Create tasks
    TaskVO research = createTask(
    "Research"
    ); TaskVO development = createTask(
    "Development"
    );
    // Create timecards
    TimecardVO timecard1 = createTimecard(naresh, john); TimecardVO timecard2 = createTimecard(naresh, john); TimecardVO timecard3 = createTimecard(louis, john); TimecardVO timecard4 = createTimecard(louis, john);
    // Fetch and show all objects created above
    PersonVO[] people = peopleService.getAllPeople(); showPeople(people); TaskVO[] tasks = timeTrackingService.getAllTasks(); showTasks(tasks); TimecardSummaryVO[] timecards = timeTrackingService.getAllTimecardSummaries(); showTimecardSummaries(timecards);
    // Fetch and show timecard1 details
    System.out.println(
    "Timecard "
    + timecard1.getId() +
    " Details:"
    ); TimecardVO timecard1FromDB = timeTrackingService.getTimecard(timecard1.getId()); showTimecard(timecard1FromDB); }
    As you can see we are now creating tasks and timecards in addition to people. The createTask() and createTimecard() methods call the TimeTrackingService to perform these functions. Following that we fetch all the people, tasks and timecards that exist in the database and display them.
  3. Make sure the console application compiles by executing the ttconsole target as follows: maven -o ttconsole. Of course, there is no hope of running this application successfully yet as much of the service layer has not been implemented. We will now try to make the application run successfully one step at a time.

Creating Tasks

TimeTrackerConsole's createTask() simply calls TimeTrackingService's createTask() which is not yet implemented. So let us implement that method first. Open TimeTrackingServiceImpl.java and fill in the handleCreateTask() method as follows:

protected
java.lang.Long handleCreateTask(org.andromda.timetracker.vo.TaskVO taskVO)
throws
java.lang.Exception { Task task = Task.Factory.newInstance(); getTaskDao().taskVOToEntity(taskVO, task,
true
); getTaskDao().create(task);
return
task.getId(); }

Review the default implementation of taskVOToEntity() in TaskDaoBase.java. This implementation should work just fine.

Now add the import statements shown below near the top of TimeTrackingServiceImpl.java (below the package statement):

package
org.andromda.timetracker.service;
import
java.util.Collection;
import
org.andromda.timetracker.domain.*;
import
org.andromda.timetracker.vo.*;

We should be able to create tasks now. Compile core by executing the command maven -o core and run the application using maven -o run. Output from this run is shown below.

C:/timetracker>maven -o run
    ...
    [java] Person 1 created - nbhatia
    [java] Person 2 created - lcoude
    [java] Person 3 created - jsmith
    [java] Task 1 created - Research
    [java] Task 2 created - Development
    [java] Exception in thread "main" java.lang.NullPointerException
    [java]      at org.andromda.timetracker.console.TimeTrackerConsole.createTimecard(TimeTrackerConsole.java:94)
    [java]      at org.andromda.timetracker.console.TimeTrackerConsole.main(TimeTrackerConsole.java:42)
    [java] [ERROR] Java Result: 1
                        

Notice that tasks are being created successfully. You can verify this by querying your database. However, we get a NullPointerException in TimeTrackerConsole.java. After looking at code in this area it is clear that timeTrackingService.getAllTasks() is returning null. This is obviously the case because that method has not been implemented yet. Implement the method as shown below by calling TaskDao.loadAll():

protected
org.andromda.timetracker.vo.TaskVO[] handleGetAllTasks()
throws
java.lang.Exception { Collection tasks = getTaskDao().loadAll(TaskDao.TRANSFORM_TASKVO);
return
(TaskVO[])tasks.toArray(
new
TaskVO[tasks.size()]); }

Compile core by executing the command maven -o core. Before we run the application again we must wipe the database and recreate the schema. Execute the command maven -o drop-schema create-schema to start with a clean database. Now run the application by executing maven -o run. Output from this run is shown below.

C:/timetracker>maven -o run
    ...
    [java] Person 1 created - nbhatia
    [java] Person 2 created - lcoude
    [java] Person 3 created - jsmith
    [java] Task 1 created - Research
    [java] Task 2 created - Development
    [java] Timecard null created with 3 allocations
    [java] Timecard null created with 1 allocations
    [java] Timecard null created with 1 allocations
    [java] Timecard null created with 1 allocations
    ...
                        

Much better! It seems that tasks are being retrieved correctly now. The next hurdle to cross is the creation of timecards.

Creating Timecards

Looking at the code in TimeTrackerConsole.createTimecard() it is clear that timeTrackingService.createTimecard() is returning null. The reason is that createTimecard() has not been implemented in TimeTrackingService. So open TimeTrackingServiceImpl.java under C:/timetracker/core/src/java/org/andromda/timetracker/service and implement handleCreateTimecard() as follows:

    
protected
java.lang.Long handleCreateTimecard(org.andromda.timetracker.vo.TimecardVO timecardVO)
throws
java.lang.Exception {
// Create new timecard from timecardVO
Timecard timecard = Timecard.Factory.newInstance(); getTimecardDao().timecardVOToEntity(timecardVO, timecard,
true
);
// Set submitter and approver associations
timecard.setSubmitter(getPersonDao().findByUsername(timecardVO.getSubmitterName())); timecard.setApprover(getPersonDao().findByUsername(timecardVO.getApproverName()));
// Set allocations
TimeAllocationVO allocations[] = timecardVO.getAllocations();
for
(
int
i=
0
; i<allocations.length; i++) {
// Create TimeAllocation from TimeAllocationVO
TimeAllocationVO allocationVO = allocations[i]; TimeAllocation allocation = TimeAllocation.Factory.newInstance(); getTimeAllocationDao().timeAllocationVOToEntity(allocationVO, allocation,
true
);
// Connect to timecard
timecard.addTimeAllocation(allocation);
// Connect to task
allocation.setTask(getTaskDao().load(allocationVO.getTaskId())); }
// Create the timecard
getTimecardDao().create(timecard);
return
timecard.getId(); }

Looking at TimeAllocationDaoBase.java, it is clear that AndroMDA does not handle conversion of embedded values when converting VOs to entities and vice-versa. We will need to write some custom code in TimeAllocationDaoImpl.java to do that conversion. This file is generated in the directory C:/timetracker/core/src/java/org/andromda/timetracker/domain. Make the following modifications to the file:

  1. Add the following import statements after the package statement.
    package
    org.andromda.timetracker.domain;
    import
    org.andromda.timetracker.vo.TimeAllocationVO;
    import
    org.andromda.timetracker.vo.TimePeriodVO;
  2. Implement the two toTimeAllocationVO() methods as follows:
        
    public
    void
    toTimeAllocationVO( org.andromda.timetracker.domain.TimeAllocation sourceEntity, org.andromda.timetracker.vo.TimeAllocationVO targetVO) {
    super
    .toTimeAllocationVO(sourceEntity, targetVO); TimePeriod timePeriod = sourceEntity.getTimePeriod(); targetVO.setTimePeriodVO(
    new
    TimePeriodVO(timePeriod.getStartTime(), timePeriod.getEndTime())); targetVO.setTaskId(sourceEntity.getTask().getId()); }
    public
    org.andromda.timetracker.vo.TimeAllocationVO toTimeAllocationVO(
    final
    org.andromda.timetracker.domain.TimeAllocation entity) { TimeAllocationVO targetVO =
    new
    TimeAllocationVO(); toTimeAllocationVO(entity, targetVO);
    return
    targetVO; }
  3. Implement the timeAllocationVOToEntity() method as follows:
        
    public
    void
    timeAllocationVOToEntity( org.andromda.timetracker.vo.TimeAllocationVO sourceVO, org.andromda.timetracker.domain.TimeAllocation targetEntity,
    boolean
    copyIfNull) {
    super
    .timeAllocationVOToEntity(sourceVO, targetEntity, copyIfNull); TimePeriodVO timePeriodVO = sourceVO.getTimePeriodVO(); targetEntity.setTimePeriod( TimePeriod.newInstance(timePeriodVO.getStartTime(), timePeriodVO.getEndTime())); }

We need similar changes to the TimecardDaoImpl class:

    • Add the following import statements after the package statement.
      package
      org.andromda.timetracker.domain;
      import
      java.util.ArrayList;
      import
      java.util.Collection;
      import
      org.andromda.timetracker.ServiceLocator;
      import
      org.andromda.timetracker.vo.TimeAllocationVO;
      import
      org.andromda.timetracker.vo.TimecardSummaryVO;
      import
      org.andromda.timetracker.vo.TimecardVO;
    • Implement the two toTimecardSummaryVO() methods as follows:
          
      public
      void
      toTimecardSummaryVO( org.andromda.timetracker.domain.Timecard sourceEntity, org.andromda.timetracker.vo.TimecardSummaryVO targetVO) {
      super
      .toTimecardSummaryVO(sourceEntity, targetVO); targetVO.setSubmitterName(sourceEntity.getSubmitter().getUsername()); targetVO.setApproverName(sourceEntity.getApprover().getUsername()); }
      public
      org.andromda.timetracker.vo.TimecardSummaryVO toTimecardSummaryVO(
      final
      org.andromda.timetracker.domain.Timecard entity) { TimecardSummaryVO targetVO =
      new
      TimecardSummaryVO(); toTimecardSummaryVO(entity, targetVO);
      return
      targetVO; }
    • Implement the two toTimecardVO() methods as follows:
          
      public
      void
      toTimecardVO( org.andromda.timetracker.domain.Timecard sourceEntity, org.andromda.timetracker.vo.TimecardVO targetVO) { toTimecardSummaryVO(sourceEntity, targetVO);
      // Create allocations
      Collection allocations =
      new
      ArrayList(sourceEntity.getAllocations()); ((TimeAllocationDao)ServiceLocator.instance().getService(
      "timeAllocationDao"
      )) .toTimeAllocationVOCollection(allocations); targetVO.setAllocations((TimeAllocationVO[])allocations .toArray(
      new
      TimeAllocationVO[allocations.size()])); }
      public
      org.andromda.timetracker.vo.TimecardVO toTimecardVO(
      final
      org.andromda.timetracker.domain.Timecard entity) { TimecardVO targetVO =
      new
      TimecardVO(); toTimecardVO(entity, targetVO);
      return
      targetVO; }

Finally we want to implement two methods in TimeTrackingServiceImpl.java so that timecards can be retrieved for display. Here are the two methods:

    
protected
org.andromda.timetracker.vo.TimecardVO handleGetTimecard(java.lang.Long id)
throws
java.lang.Exception {
return
(TimecardVO)getTimecardDao().load(TimecardDao.TRANSFORM_TIMECARDVO, id); }
protected
org.andromda.timetracker.vo.TimecardSummaryVO[] handleGetAllTimecardSummaries()
throws
java.lang.Exception { Collection timecards = getTimecardDao().loadAll(TimecardDao.TRANSFORM_TIMECARDSUMMARYVO);
return
(TimecardSummaryVO[])timecards.toArray(
new
TimecardSummaryVO[timecards.size()]); }

Compile the application by executing the command maven -o clean install. Before we run the application again we must wipe the database and recreate the schema. Execute the command maven -o drop-schema create-schema to start with a clean database. Now run the application by executing maven -o run. The application runs successfully all the way! Output from this run is shown below.

    [java] Person 1 created - nbhatia
    [java] Person 2 created - lcoude
    [java] Person 3 created - jsmith
    [java] Task 1 created - Research
    [java] Task 2 created - Development
    [java] Timecard 1 created with 3 allocations
    [java] Timecard 2 created with 1 allocations
    [java] Timecard 3 created with 2 allocations
    [java] Timecard 4 created with 2 allocations
    [java] People:
    [java] 1: nbhatia - Naresh Bhatia
    [java] 2: lcoude - Louis Coude
    [java] 3: jsmith - John Smith
    [java]
    [java] Tasks:
    [java] 1: Research
    [java] 2: Development
    [java]
    [java] Timecards:
    [java] 1: status=DRAFT, begin date=2006-02-01 04:23:33.0, comments=On track!, submitter=nbhatia, approver=jsmith
    [java] 2: status=DRAFT, begin date=2006-02-01 04:23:33.0, comments=On track!, submitter=nbhatia, approver=jsmith
    [java] 3: status=DRAFT, begin date=2006-02-01 04:23:34.0, comments=On track!, submitter=lcoude, approver=jsmith
    [java] 4: status=DRAFT, begin date=2006-02-01 04:23:34.0, comments=On track!, submitter=lcoude, approver=jsmith
    [java]
    [java] Timecard 1 Details:
    [java] 1: status=DRAFT, begin date=2006-02-01 04:23:33.0, comments=On track!, submitter=nbhatia, approver=jsmith
    [java]     1: start time=2006-02-03 04:23:33.0, end time=2006-02-04 04:23:33.0, task id=2
    [java]     2: start time=2006-02-02 04:23:33.0, end time=2006-02-03 04:23:33.0, task id=1
    [java]     3: start time=2006-02-01 04:23:33.0, end time=2006-02-02 04:23:33.0, task id=1
                        

Where to go from here?

In this guide we have made sure we have everything installed to start writing MDA projects using AndroMDA, but that's just the start .. there are several different cartridges you can use, or maybe, if you can't find one you like, you can write one yourself and contribute to the AndroMDA project.

Another thing you can try is to build a GUI front-end for the TimeTracker application. You can refer to the How-to Guides to learn about the Bpm4Struts cartridge which can be used to build Struts front-ends.

In any case you will need to learn about UML, MDA and modeling techniques and best practices. Perhaps a good idea would be to take a look at the samples included in the binary distribution, each one of them includes a model. Such a distribution is found here: AndroMDA project page . Please note that the samples in the distribution still need to be built if you want to see the generated sources or ant to deploy it (just go into a sample and invoke maven, nothing more to it).