Просмотр исходного кода

HandlingEventService.registerHandlingEvent() signature takes value objects instead of HandlingEventRegistrationAttempt, decoupling it from message queues.

Creating sample data using domain objects and storing with Hibernate, for simplicity.

Estimated time of arrival is now a property of the Cargo aggregate.

Tracking web interface has new look for 1.1.

Admin interface shows more time related information.

Serverside now handles change of destination (no UI yet though)
peter_backlund 17 лет назад
Родитель
Сommit
61b7fe0138
39 измененных файлов: 533 добавлений и 219 удалений
  1. 2
    2
      dddsample/pom.xml
  2. 0
    9
      dddsample/src/main/java/se/citerus/dddsample/application/ApplicationEvents.java
  3. 10
    1
      dddsample/src/main/java/se/citerus/dddsample/application/BookingService.java
  4. 13
    2
      dddsample/src/main/java/se/citerus/dddsample/application/HandlingEventService.java
  5. 19
    1
      dddsample/src/main/java/se/citerus/dddsample/application/impl/BookingServiceImpl.java
  6. 12
    9
      dddsample/src/main/java/se/citerus/dddsample/application/impl/HandlingEventServiceImpl.java
  7. 1
    1
      dddsample/src/main/java/se/citerus/dddsample/application/util/DateTestUtil.java
  8. 132
    1
      dddsample/src/main/java/se/citerus/dddsample/application/util/SampleDataGenerator.java
  9. 61
    28
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Cargo.java
  10. 4
    4
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Itinerary.java
  11. 1
    1
      dddsample/src/main/java/se/citerus/dddsample/domain/model/carrier/SampleVoyages.java
  12. 2
    2
      dddsample/src/main/java/se/citerus/dddsample/domain/model/location/SampleLocations.java
  13. 8
    1
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/messaging/jms/HandlingEventRegistrationAttemptConsumer.java
  14. 0
    12
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/messaging/jms/JmsApplicationEventsImpl.java
  15. 5
    3
      dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/BookingServiceFacade.java
  16. 18
    3
      dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/dto/CargoRoutingDTO.java
  17. 16
    1
      dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/dto/LegDTO.java
  18. 2
    2
      dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/dto/RouteCandidateDTO.java
  19. 18
    7
      dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/internal/BookingServiceFacadeImpl.java
  20. 7
    4
      dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/internal/assembler/CargoRoutingDTOAssembler.java
  21. 7
    7
      dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/internal/assembler/ItineraryCandidateDTOAssembler.java
  22. 32
    31
      dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/web/CargoAdminController.java
  23. 42
    7
      dddsample/src/main/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingViewAdapter.java
  24. 9
    1
      dddsample/src/main/resources/messages_en.properties
  25. 2
    1
      dddsample/src/main/resources/se/citerus/dddsample/infrastructure/persistence/hibernate/Cargo.hbm.xml
  26. 5
    8
      dddsample/src/main/webapp/WEB-INF/jsp/admin/selectItinerary.jsp
  27. 23
    5
      dddsample/src/main/webapp/WEB-INF/jsp/admin/show.jsp
  28. 16
    4
      dddsample/src/main/webapp/WEB-INF/jsp/pub/track.jsp
  29. 2
    0
      dddsample/src/main/webapp/WEB-INF/jspf/include.jspf
  30. 5
    3
      dddsample/src/main/webapp/style.css
  31. 19
    21
      dddsample/src/test/java/se/citerus/dddsample/application/HandlingEventServiceTest.java
  32. 1
    1
      dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/CargoTest.java
  33. 1
    1
      dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/RouteSpecificationTest.java
  34. 0
    6
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/messaging/stub/SynchronousApplicationEventsStub.java
  35. 14
    1
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/AbstractRepositoryTest.java
  36. 0
    6
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryTest.java
  37. 5
    5
      dddsample/src/test/java/se/citerus/dddsample/interfaces/booking/facade/internal/assembler/ItineraryCandidateDTOAssemblerTest.java
  38. 6
    6
      dddsample/src/test/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingViewAdapterTest.java
  39. 13
    11
      dddsample/src/test/java/se/citerus/dddsample/scenario/CargoLifecycleScenarioTest.java

+ 2
- 2
dddsample/pom.xml Просмотреть файл

@@ -101,8 +101,8 @@
101 101
       <plugin>
102 102
         <artifactId>maven-compiler-plugin</artifactId>
103 103
         <configuration>
104
-          <source>1.5</source>
105
-          <target>1.5</target>
104
+          <source>1.6</source>
105
+          <target>1.6</target>
106 106
           <encoding>UTF-8</encoding>
107 107
         </configuration>
108 108
       </plugin>

+ 0
- 9
dddsample/src/main/java/se/citerus/dddsample/application/ApplicationEvents.java Просмотреть файл

@@ -1,7 +1,6 @@
1 1
 package se.citerus.dddsample.application;
2 2
 
3 3
 import se.citerus.dddsample.domain.model.cargo.Cargo;
4
-import se.citerus.dddsample.domain.model.handling.CannotCreateHandlingEventException;
5 4
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
6 5
 
7 6
 /**
@@ -41,12 +40,4 @@ public interface ApplicationEvents {
41 40
    */
42 41
   void receivedHandlingEventRegistrationAttempt(HandlingEventRegistrationAttempt attempt);
43 42
 
44
-  /**
45
-   * A handling event regitration attempt is rejected for a certain reason.
46
-   *
47
-   * @param attempt handling event registration attempt
48
-   * @param cause cause of rejection
49
-   */
50
-  void rejectedHandlingEventRegistrationAttempt(HandlingEventRegistrationAttempt attempt, CannotCreateHandlingEventException cause);
51
-
52 43
 }

+ 10
- 1
dddsample/src/main/java/se/citerus/dddsample/application/BookingService.java Просмотреть файл

@@ -32,7 +32,16 @@ public interface BookingService {
32 32
 
33 33
   /**
34 34
    * @param itinerary itinerary describing the selected route
35
-   * @param trackingId tracking id
35
+   * @param trackingId cargo tracking id
36 36
    */
37 37
   void assignCargoToRoute(Itinerary itinerary, TrackingId trackingId);
38
+
39
+  /**
40
+   * Changes the destination of a cargo.
41
+   *
42
+   * @param trackingId cargo tracking id
43
+   * @param unLocode UN locode of new destination
44
+   */
45
+  void changeDestination(TrackingId trackingId, UnLocode unLocode);
46
+
38 47
 }

+ 13
- 2
dddsample/src/main/java/se/citerus/dddsample/application/HandlingEventService.java Просмотреть файл

@@ -1,5 +1,12 @@
1 1
 package se.citerus.dddsample.application;
2 2
 
3
+import se.citerus.dddsample.domain.model.cargo.TrackingId;
4
+import se.citerus.dddsample.domain.model.carrier.VoyageNumber;
5
+import se.citerus.dddsample.domain.model.handling.HandlingEvent;
6
+import se.citerus.dddsample.domain.model.location.UnLocode;
7
+
8
+import java.util.Date;
9
+
3 10
 /**
4 11
  * Handling event service.
5 12
  */
@@ -9,8 +16,12 @@ public interface HandlingEventService {
9 16
    * Registers a handling event in the system, and notifies interested
10 17
    * parties that a cargo has been handled.
11 18
    *
12
-   * @param attempt handling event registration attempt
19
+   * @param completionTime when the event was completed
20
+   * @param trackingId cargo tracking id
21
+   * @param voyageNumber voyage number
22
+   * @param unLocode UN locode for the location where the event occurred
23
+   * @param type type of event
13 24
    */
14
-  void registerHandlingEvent(HandlingEventRegistrationAttempt attempt);
25
+  void registerHandlingEvent(Date completionTime, TrackingId trackingId, VoyageNumber voyageNumber, UnLocode unLocode, HandlingEvent.Type type);
15 26
 
16 27
 }

+ 19
- 1
dddsample/src/main/java/se/citerus/dddsample/application/impl/BookingServiceImpl.java Просмотреть файл

@@ -82,5 +82,23 @@ public final class BookingServiceImpl implements BookingService {
82 82
 
83 83
     logger.info("Assigned cargo " + trackingId + " to new route");
84 84
   }
85
-  
85
+
86
+  @Override
87
+  @Transactional
88
+  public void changeDestination(final TrackingId trackingId, final UnLocode unLocode) {
89
+    Validate.notNull(trackingId);
90
+    Validate.notNull(unLocode);
91
+
92
+    final Cargo cargo = cargoRepository.find(trackingId);
93
+    final Location newDestination = locationRepository.find(unLocode);
94
+
95
+    final RouteSpecification routeSpecification = new RouteSpecification(
96
+      cargo.origin(), newDestination, cargo.routeSpecification().arrivalDeadline()
97
+    );
98
+    cargo.specifyNewRoute(routeSpecification);
99
+
100
+    cargoRepository.store(cargo);
101
+    logger.info("Changed destination for cargo " + trackingId + " to " + routeSpecification.destination());
102
+  }
103
+
86 104
 }

+ 12
- 9
dddsample/src/main/java/se/citerus/dddsample/application/impl/HandlingEventServiceImpl.java Просмотреть файл

@@ -4,12 +4,16 @@ import org.apache.commons.logging.Log;
4 4
 import org.apache.commons.logging.LogFactory;
5 5
 import org.springframework.transaction.annotation.Transactional;
6 6
 import se.citerus.dddsample.application.ApplicationEvents;
7
-import se.citerus.dddsample.application.HandlingEventRegistrationAttempt;
8 7
 import se.citerus.dddsample.application.HandlingEventService;
8
+import se.citerus.dddsample.domain.model.cargo.TrackingId;
9
+import se.citerus.dddsample.domain.model.carrier.VoyageNumber;
9 10
 import se.citerus.dddsample.domain.model.handling.CannotCreateHandlingEventException;
10 11
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
11 12
 import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
12 13
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
14
+import se.citerus.dddsample.domain.model.location.UnLocode;
15
+
16
+import java.util.Date;
13 17
 
14 18
 public final class HandlingEventServiceImpl implements HandlingEventService {
15 19
 
@@ -28,18 +32,17 @@ public final class HandlingEventServiceImpl implements HandlingEventService {
28 32
 
29 33
   @Override
30 34
   @Transactional
31
-  public void registerHandlingEvent(final HandlingEventRegistrationAttempt attempt) {
35
+  public void registerHandlingEvent(final Date completionTime,
36
+                                    final TrackingId trackingId,
37
+                                    final VoyageNumber voyageNumber,
38
+                                    final UnLocode unLocode,
39
+                                    final HandlingEvent.Type type) {
32 40
     try {
33 41
       /* Using a factory to create a HandlingEvent (aggregate). This is where
34 42
          it is determined wether the incoming data, the attempt, actually is capable
35 43
          of representing a real handling event. */
36 44
       final HandlingEvent event = handlingEventFactory.createHandlingEvent(
37
-        attempt.getRegistrationTime(),
38
-        attempt.getCompletionTime(),
39
-        attempt.getTrackingId(),
40
-        attempt.getVoyageNumber(),
41
-        attempt.getUnLocode(),
42
-        attempt.getType()
45
+        new Date(), completionTime, trackingId, voyageNumber, unLocode, type
43 46
       );
44 47
 
45 48
       /* Store the new handling event, which updates the persistent
@@ -55,7 +58,7 @@ public final class HandlingEventServiceImpl implements HandlingEventService {
55 58
     } catch (CannotCreateHandlingEventException e) {
56 59
       /* This may be a bogus attempt, for example containing a tracking id
57 60
          that doesn't match any cargo that we're tracking. */
58
-      applicationEvents.rejectedHandlingEventRegistrationAttempt(attempt, e);
61
+      logger.error(e, e);
59 62
     }
60 63
   }
61 64
 

dddsample/src/test/java/se/citerus/dddsample/DateTestUtil.java → dddsample/src/main/java/se/citerus/dddsample/application/util/DateTestUtil.java Просмотреть файл

@@ -1,4 +1,4 @@
1
-package se.citerus.dddsample;
1
+package se.citerus.dddsample.application.util;
2 2
 
3 3
 import java.text.ParseException;
4 4
 import java.text.SimpleDateFormat;

+ 132
- 1
dddsample/src/main/java/se/citerus/dddsample/application/util/SampleDataGenerator.java Просмотреть файл

@@ -1,5 +1,7 @@
1 1
 package se.citerus.dddsample.application.util;
2 2
 
3
+import org.hibernate.SessionFactory;
4
+import org.hibernate.classic.Session;
3 5
 import org.springframework.beans.factory.BeanFactoryUtils;
4 6
 import org.springframework.jdbc.core.JdbcTemplate;
5 7
 import org.springframework.transaction.PlatformTransactionManager;
@@ -8,6 +10,18 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult;
8 10
 import org.springframework.transaction.support.TransactionTemplate;
9 11
 import org.springframework.web.context.WebApplicationContext;
10 12
 import org.springframework.web.context.support.WebApplicationContextUtils;
13
+import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
14
+import se.citerus.dddsample.domain.model.cargo.*;
15
+import static se.citerus.dddsample.domain.model.carrier.SampleVoyages.*;
16
+import se.citerus.dddsample.domain.model.carrier.VoyageRepository;
17
+import se.citerus.dddsample.domain.model.handling.CannotCreateHandlingEventException;
18
+import se.citerus.dddsample.domain.model.handling.HandlingEvent;
19
+import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
20
+import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
21
+import se.citerus.dddsample.domain.model.location.Location;
22
+import se.citerus.dddsample.domain.model.location.LocationRepository;
23
+import se.citerus.dddsample.domain.model.location.SampleLocations;
24
+import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
11 25
 
12 26
 import javax.servlet.ServletContextEvent;
13 27
 import javax.servlet.ServletContextListener;
@@ -15,7 +29,9 @@ import javax.sql.DataSource;
15 29
 import java.sql.Timestamp;
16 30
 import java.text.ParseException;
17 31
 import java.text.SimpleDateFormat;
32
+import static java.util.Arrays.asList;
18 33
 import java.util.Date;
34
+import java.util.List;
19 35
 
20 36
 /**
21 37
  * Provides sample data.
@@ -173,7 +189,122 @@ public class SampleDataGenerator implements ServletContextListener {
173 189
     WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(event.getServletContext());
174 190
     DataSource dataSource = (DataSource) BeanFactoryUtils.beanOfType(context, DataSource.class);
175 191
     PlatformTransactionManager transactionManager = (PlatformTransactionManager) BeanFactoryUtils.beanOfType(context, PlatformTransactionManager.class);
176
-    loadSampleData(new JdbcTemplate(dataSource), new TransactionTemplate(transactionManager));
192
+    TransactionTemplate tt = new TransactionTemplate(transactionManager);
193
+    //loadSampleData(new JdbcTemplate(dataSource), tt);
194
+
195
+
196
+    SessionFactory sf = (SessionFactory) BeanFactoryUtils.beanOfType(context, SessionFactory.class);
197
+    HandlingEventFactory handlingEventFactory = new HandlingEventFactory(
198
+      getBean(context, CargoRepository.class),
199
+      getBean(context, VoyageRepository.class),
200
+      getBean(context, LocationRepository.class));
201
+    loadHibernateData(tt, sf, handlingEventFactory, getBean(context, HandlingEventRepository.class));
202
+  }
203
+
204
+  private <T> T getBean(WebApplicationContext context, Class<T> cls) {
205
+    return (T) BeanFactoryUtils.beanOfType(context, cls);
206
+  }
207
+
208
+  public static void loadHibernateData(TransactionTemplate tt, final SessionFactory sf, final HandlingEventFactory handlingEventFactory, final HandlingEventRepository handlingEventRepository) {
209
+    System.out.println("*** Loading Hibernate data ***");
210
+    tt.execute(new TransactionCallbackWithoutResult() {
211
+      @Override
212
+      protected void doInTransactionWithoutResult(TransactionStatus status) {
213
+        Session session = sf.getCurrentSession();
214
+
215
+        for (Location location : SampleLocations.getAll()) {
216
+          session.save(location);
217
+        }
218
+
219
+        session.save(HONGKONG_TO_NEW_YORK);
220
+        session.save(NEW_YORK_TO_DALLAS);
221
+        session.save(DALLAS_TO_HELSINKI);
222
+        session.save(HELSINKI_TO_HONGKONG);
223
+        session.save(DALLAS_TO_HELSINKI_ALT);
224
+
225
+        RouteSpecification routeSpecification = new RouteSpecification(HONGKONG, HELSINKI, toDate("2009-03-15"));
226
+        TrackingId trackingId = new TrackingId("ABC123");
227
+        Cargo abc123 = new Cargo(trackingId, HELSINKI, routeSpecification);
228
+
229
+        Itinerary itinerary = new Itinerary(asList(
230
+          new Leg(HONGKONG_TO_NEW_YORK, HONGKONG, NEWYORK, toDate("2009-03-02"), toDate("2009-03-05")),
231
+          new Leg(NEW_YORK_TO_DALLAS, NEWYORK, DALLAS, toDate("2009-03-06"), toDate("2009-03-08")),
232
+          new Leg(DALLAS_TO_HELSINKI, DALLAS, HELSINKI, toDate("2009-03-09"), toDate("2009-03-12"))
233
+        ));
234
+        abc123.assignToRoute(itinerary);
235
+
236
+        session.save(abc123);
237
+        
238
+        try {
239
+          HandlingEvent event1 = handlingEventFactory.createHandlingEvent(
240
+            new Date(), toDate("2009-03-01"), trackingId, null, HONGKONG.unLocode(), HandlingEvent.Type.RECEIVE
241
+          );
242
+          session.save(event1);
243
+
244
+          HandlingEvent event2 = handlingEventFactory.createHandlingEvent(
245
+            new Date(), toDate("2009-03-02"), trackingId, HONGKONG_TO_NEW_YORK.voyageNumber(), HONGKONG.unLocode(), HandlingEvent.Type.LOAD
246
+          );
247
+          session.save(event2);
248
+
249
+          HandlingEvent event3 = handlingEventFactory.createHandlingEvent(
250
+            new Date(), toDate("2009-03-05"), trackingId, HONGKONG_TO_NEW_YORK.voyageNumber(), NEWYORK.unLocode(), HandlingEvent.Type.UNLOAD
251
+          );
252
+          session.save(event3);
253
+        } catch (CannotCreateHandlingEventException e) {
254
+          throw new RuntimeException(e);
255
+        }
256
+
257
+        List<HandlingEvent> handlingEvents = handlingEventRepository.findEventsForCargo(trackingId);
258
+        abc123.deriveStatusFromHandling(handlingEvents);
259
+
260
+        session.update(abc123);
261
+
262
+        // Cargo JKL567
263
+
264
+        RouteSpecification routeSpecification1 = new RouteSpecification(HANGZOU, STOCKHOLM, toDate("2009-03-18"));
265
+        TrackingId trackingId1 = new TrackingId("JKL567");
266
+        Cargo jkl567 = new Cargo(trackingId1, HANGZOU, routeSpecification1);
267
+
268
+        Itinerary itinerary1 = new Itinerary(asList(
269
+          new Leg(HONGKONG_TO_NEW_YORK, HANGZOU, NEWYORK, toDate("2009-03-03"), toDate("2009-03-05")),
270
+          new Leg(NEW_YORK_TO_DALLAS, NEWYORK, DALLAS, toDate("2009-03-06"), toDate("2009-03-08")),
271
+          new Leg(DALLAS_TO_HELSINKI, DALLAS, STOCKHOLM, toDate("2009-03-09"), toDate("2009-03-11"))
272
+        ));
273
+        jkl567.assignToRoute(itinerary1);
274
+
275
+        session.save(jkl567);
276
+
277
+        try {
278
+          HandlingEvent event1 = handlingEventFactory.createHandlingEvent(
279
+            new Date(), toDate("2009-03-01"), trackingId1, null, HANGZOU.unLocode(), HandlingEvent.Type.RECEIVE
280
+          );
281
+          session.save(event1);
282
+
283
+          HandlingEvent event2 = handlingEventFactory.createHandlingEvent(
284
+            new Date(), toDate("2009-03-03"), trackingId1, HONGKONG_TO_NEW_YORK.voyageNumber(), HANGZOU.unLocode(), HandlingEvent.Type.LOAD
285
+          );
286
+          session.save(event2);
287
+
288
+          HandlingEvent event3 = handlingEventFactory.createHandlingEvent(
289
+            new Date(), toDate("2009-03-05"), trackingId1, HONGKONG_TO_NEW_YORK.voyageNumber(), NEWYORK.unLocode(), HandlingEvent.Type.UNLOAD
290
+          );
291
+          session.save(event3);
292
+
293
+          HandlingEvent event4 = handlingEventFactory.createHandlingEvent(
294
+            new Date(), toDate("2009-03-06"), trackingId1, HONGKONG_TO_NEW_YORK.voyageNumber(), NEWYORK.unLocode(), HandlingEvent.Type.LOAD
295
+          );
296
+          session.save(event4);
297
+
298
+        } catch (CannotCreateHandlingEventException e) {
299
+          throw new RuntimeException(e);
300
+        }
301
+
302
+        List<HandlingEvent> handlingEvents1 = handlingEventRepository.findEventsForCargo(trackingId1);
303
+        jkl567.deriveStatusFromHandling(handlingEvents1);
304
+
305
+        session.update(jkl567);
306
+      }
307
+    });
177 308
   }
178 309
 
179 310
   public void contextDestroyed(ServletContextEvent event) {}

+ 61
- 28
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Cargo.java Просмотреть файл

@@ -8,6 +8,7 @@ import se.citerus.dddsample.domain.model.location.Location;
8 8
 import se.citerus.dddsample.domain.shared.DomainObjectUtils;
9 9
 
10 10
 import java.util.Collections;
11
+import java.util.Date;
11 12
 import java.util.List;
12 13
 
13 14
 /**
@@ -47,6 +48,9 @@ public class Cargo implements Entity<Cargo> {
47 48
   private RouteSpecification routeSpecification;
48 49
   private RoutingStatus routingStatus;
49 50
   private boolean misdirected;
51
+  private Date eta;
52
+  
53
+  private static final Date ETA_UNKOWN = null;
50 54
 
51 55
   // TODO origin can be taken from route spec on creation, even if the origin never changes
52 56
   public Cargo(final TrackingId trackingId, final Location origin, final RouteSpecification routeSpecification) {
@@ -122,6 +126,7 @@ public class Cargo implements Entity<Cargo> {
122 126
     // Handling consistency within the Cargo aggregate synchronously
123 127
     this.routingStatus = deriveRoutingStatus();
124 128
     this.misdirected = deriveMisdirectionStatus();
129
+    this.eta = deriveEta();
125 130
   }
126 131
 
127 132
   /**
@@ -139,16 +144,6 @@ public class Cargo implements Entity<Cargo> {
139 144
     return misdirected;
140 145
   }
141 146
 
142
-  private boolean deriveMisdirectionStatus() {
143
-    final HandlingEvent lastEvent = delivery().lastEvent();
144
-    if (lastEvent == null) {
145
-      return false;
146
-    } else {
147
-      return !itinerary().isExpected(lastEvent);
148
-    }
149
-  }
150
-
151
-
152 147
   /**
153 148
    * @return Routing status.
154 149
    */
@@ -157,29 +152,20 @@ public class Cargo implements Entity<Cargo> {
157 152
   }
158 153
 
159 154
   /**
160
-   * Updates the routing status.
161
-   * @return current routing status
162
-   */
163
-  private RoutingStatus deriveRoutingStatus() {
164
-    if (itinerary == null) {
165
-      return NOT_ROUTED;
166
-    } else {
167
-      if (routeSpecification.isSatisfiedBy(itinerary)) {
168
-        return ROUTED;
169
-      } else {
170
-        return MISROUTED;
171
-      }
172
-    }
173
-  }
174
-
175
-  /**
176 155
    * @return True if the cargo has been unloaded at the final destination.
177 156
    */
178 157
   public boolean isUnloadedAtDestination() {
179 158
     final HandlingEvent lastEvent = delivery.lastEvent();
180 159
     return lastEvent != null &&
181
-           HandlingEvent.Type.UNLOAD.sameValueAs(lastEvent.type()) &&
182
-           routeSpecification.destination().sameIdentityAs(lastEvent.location());
160
+      HandlingEvent.Type.UNLOAD.sameValueAs(lastEvent.type()) &&
161
+      routeSpecification.destination().sameIdentityAs(lastEvent.location());
162
+  }
163
+
164
+  /**
165
+   * @return estimated time of arrival
166
+   */
167
+  public Date estimatedTimeOfArrival() {
168
+    return eta;
183 169
   }
184 170
 
185 171
   /**
@@ -203,8 +189,55 @@ public class Cargo implements Entity<Cargo> {
203 189
     this.delivery = Delivery.derivedFrom(deliveryHistory);
204 190
     this.routingStatus = deriveRoutingStatus();
205 191
     this.misdirected = deriveMisdirectionStatus();
192
+    this.eta = deriveEta();
206 193
   }
207 194
 
195
+  /**
196
+   *
197
+   * @return true if this cargo is misdirected.
198
+   */
199
+  private boolean deriveMisdirectionStatus() {
200
+    final HandlingEvent lastEvent = delivery().lastEvent();
201
+    if (lastEvent == null) {
202
+      return false;
203
+    } else {
204
+      return !itinerary().isExpected(lastEvent);
205
+    }
206
+  }
207
+
208
+  /**
209
+   * @return current routing status
210
+   */
211
+  private RoutingStatus deriveRoutingStatus() {
212
+    if (itinerary == null) {
213
+      return NOT_ROUTED;
214
+    } else {
215
+      if (routeSpecification.isSatisfiedBy(itinerary)) {
216
+        return ROUTED;
217
+      } else {
218
+        return MISROUTED;
219
+      }
220
+    }
221
+  }
222
+
223
+  /**
224
+   * @return estimated time of arrival, or null if unknown
225
+   */
226
+  private Date deriveEta() {
227
+    if (onTrack()) {
228
+      return itinerary().finalArrivalDate();
229
+    } else {
230
+      return ETA_UNKOWN;
231
+    }
232
+  }
233
+
234
+  /**
235
+   * @return true if cargo is on track, i.e. routed and not misdirected
236
+   */
237
+  private boolean onTrack() {
238
+    return routingStatus().equals(ROUTED) && !misdirected;
239
+  }
240
+  
208 241
   @Override
209 242
   public boolean sameIdentityAs(final Cargo other) {
210 243
     return other != null && trackingId.sameValueAs(other.trackingId);

+ 4
- 4
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Itinerary.java Просмотреть файл

@@ -89,7 +89,7 @@ public class Itinerary implements ValueObject<Itinerary> {
89 89
   /**
90 90
    * @return The initial departure location.
91 91
    */
92
-  public Location initialDepartureLocation() {
92
+  Location initialDepartureLocation() {
93 93
      if (legs.isEmpty()) {
94 94
        return Location.UNKNOWN;
95 95
      } else {
@@ -100,7 +100,7 @@ public class Itinerary implements ValueObject<Itinerary> {
100 100
   /**
101 101
    * @return The final arrival location.
102 102
    */
103
-  public Location finalArrivalLocation() {
103
+  Location finalArrivalLocation() {
104 104
     if (legs.isEmpty()) {
105 105
       return Location.UNKNOWN;
106 106
     } else {
@@ -111,7 +111,7 @@ public class Itinerary implements ValueObject<Itinerary> {
111 111
   /**
112 112
    * @return Date when cargo arrives at final destination.
113 113
    */
114
-  public Date finalArrivalDate() {
114
+  Date finalArrivalDate() {
115 115
     final Leg lastLeg = lastLeg();
116 116
 
117 117
     if (lastLeg == null) {
@@ -124,7 +124,7 @@ public class Itinerary implements ValueObject<Itinerary> {
124 124
   /**
125 125
    * @return The last leg on the itinerary.
126 126
    */
127
-  public Leg lastLeg() {
127
+  Leg lastLeg() {
128 128
     if (legs.isEmpty()) {
129 129
       return null;
130 130
     } else {

dddsample/src/test/java/se/citerus/dddsample/domain/model/carrier/SampleVoyages.java → dddsample/src/main/java/se/citerus/dddsample/domain/model/carrier/SampleVoyages.java Просмотреть файл

@@ -1,6 +1,6 @@
1 1
 package se.citerus.dddsample.domain.model.carrier;
2 2
 
3
-import static se.citerus.dddsample.DateTestUtil.toDate;
3
+import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
4 4
 import se.citerus.dddsample.domain.model.location.Location;
5 5
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
6 6
 

dddsample/src/test/java/se/citerus/dddsample/domain/model/location/SampleLocations.java → dddsample/src/main/java/se/citerus/dddsample/domain/model/location/SampleLocations.java Просмотреть файл

@@ -26,7 +26,7 @@ public class SampleLocations {
26 26
   public static final Location NEWYORK = new Location(new UnLocode("USNYC"), "New York");
27 27
   public static final Location DALLAS = new Location(new UnLocode("USDAL"), "Dallas");
28 28
 
29
-  public static final Map<UnLocode, Location> ALL = new HashMap();
29
+  public static final Map<UnLocode, Location> ALL = new HashMap<UnLocode, Location>();
30 30
 
31 31
   static {
32 32
     for (Field field : SampleLocations.class.getDeclaredFields()) {
@@ -42,7 +42,7 @@ public class SampleLocations {
42 42
   }
43 43
 
44 44
   public static List<Location> getAll() {
45
-    return new ArrayList(ALL.values());
45
+    return new ArrayList<Location>(ALL.values());
46 46
   }
47 47
 
48 48
   public static Location lookup(UnLocode unLocode) {

+ 8
- 1
dddsample/src/main/java/se/citerus/dddsample/infrastructure/messaging/jms/HandlingEventRegistrationAttemptConsumer.java Просмотреть файл

@@ -23,7 +23,14 @@ public class HandlingEventRegistrationAttemptConsumer implements MessageListener
23 23
   public void onMessage(final Message message) {
24 24
     try {
25 25
       final ObjectMessage om = (ObjectMessage) message;
26
-      handlingEventService.registerHandlingEvent((HandlingEventRegistrationAttempt) om.getObject());
26
+      HandlingEventRegistrationAttempt attempt = (HandlingEventRegistrationAttempt) om.getObject();
27
+      handlingEventService.registerHandlingEvent(
28
+        attempt.getCompletionTime(),
29
+        attempt.getTrackingId(),
30
+        attempt.getVoyageNumber(),
31
+        attempt.getUnLocode(),
32
+        attempt.getType()
33
+      );
27 34
     } catch (Exception e) {
28 35
       logger.error(e, e);
29 36
     }

+ 0
- 12
dddsample/src/main/java/se/citerus/dddsample/infrastructure/messaging/jms/JmsApplicationEventsImpl.java Просмотреть файл

@@ -7,7 +7,6 @@ import org.springframework.jms.core.MessageCreator;
7 7
 import se.citerus.dddsample.application.ApplicationEvents;
8 8
 import se.citerus.dddsample.application.HandlingEventRegistrationAttempt;
9 9
 import se.citerus.dddsample.domain.model.cargo.Cargo;
10
-import se.citerus.dddsample.domain.model.handling.CannotCreateHandlingEventException;
11 10
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
12 11
 
13 12
 import javax.jms.Destination;
@@ -61,17 +60,6 @@ public final class JmsApplicationEventsImpl implements ApplicationEvents {
61 60
   }
62 61
 
63 62
   @Override
64
-  public void rejectedHandlingEventRegistrationAttempt(final HandlingEventRegistrationAttempt attempt, CannotCreateHandlingEventException problem) {
65
-    // TODO include error message in JMS message
66
-    logger.info("Rejected handling event registration attempt " + attempt + ", " + problem);
67
-    jmsOperations.send(rejectedRegistrationAttemptsQueue, new MessageCreator() {
68
-      public Message createMessage(Session session) throws JMSException {
69
-        return session.createObjectMessage(attempt);
70
-      }
71
-    });
72
-  }
73
-
74
-  @Override
75 63
   public void receivedHandlingEventRegistrationAttempt(final HandlingEventRegistrationAttempt attempt) {
76 64
     logger.info("Received handling event registration attempt " + attempt);
77 65
     jmsOperations.send(handlingEventQueue, new MessageCreator() {

+ 5
- 3
dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/BookingServiceFacade.java Просмотреть файл

@@ -1,8 +1,8 @@
1 1
 package se.citerus.dddsample.interfaces.booking.facade;
2 2
 
3 3
 import se.citerus.dddsample.interfaces.booking.facade.dto.CargoRoutingDTO;
4
-import se.citerus.dddsample.interfaces.booking.facade.dto.ItineraryCandidateDTO;
5 4
 import se.citerus.dddsample.interfaces.booking.facade.dto.LocationDTO;
5
+import se.citerus.dddsample.interfaces.booking.facade.dto.RouteCandidateDTO;
6 6
 
7 7
 import java.rmi.Remote;
8 8
 import java.rmi.RemoteException;
@@ -19,9 +19,11 @@ public interface BookingServiceFacade extends Remote {
19 19
 
20 20
   CargoRoutingDTO loadCargoForRouting(String trackingId) throws RemoteException;
21 21
 
22
-  void assignCargoToRoute(String trackingId, ItineraryCandidateDTO itinerary) throws RemoteException;
22
+  void assignCargoToRoute(String trackingId, RouteCandidateDTO route) throws RemoteException;
23 23
 
24
-  List<ItineraryCandidateDTO> requestPossibleRoutesForCargo(String trackingId) throws RemoteException;
24
+  void changeDestination(String trackingId, String destinationUnLocode) throws RemoteException;
25
+
26
+  List<RouteCandidateDTO> requestPossibleRoutesForCargo(String trackingId) throws RemoteException;
25 27
 
26 28
   List<LocationDTO> listShippingLocations() throws RemoteException;
27 29
 

+ 18
- 3
dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/dto/CargoRoutingDTO.java Просмотреть файл

@@ -3,6 +3,7 @@ package se.citerus.dddsample.interfaces.booking.facade.dto;
3 3
 import java.io.Serializable;
4 4
 import java.util.ArrayList;
5 5
 import java.util.Collections;
6
+import java.util.Date;
6 7
 import java.util.List;
7 8
 
8 9
 /**
@@ -13,6 +14,8 @@ public final class CargoRoutingDTO implements Serializable {
13 14
   private final String trackingId;
14 15
   private final String origin;
15 16
   private final String finalDestination;
17
+  private final Date arrivalDeadline;
18
+  private final boolean misrouted;
16 19
   private final List<LegDTO> legs;
17 20
 
18 21
   /**
@@ -21,11 +24,15 @@ public final class CargoRoutingDTO implements Serializable {
21 24
    * @param trackingId
22 25
    * @param origin
23 26
    * @param finalDestination
27
+   * @param arrivalDeadline
28
+   * @param misrouted
24 29
    */
25
-  public CargoRoutingDTO(final String trackingId, final String origin, final String finalDestination) {
30
+  public CargoRoutingDTO(String trackingId, String origin, String finalDestination, Date arrivalDeadline, boolean misrouted) {
26 31
     this.trackingId = trackingId;
27 32
     this.origin = origin;
28 33
     this.finalDestination = finalDestination;
34
+    this.arrivalDeadline = arrivalDeadline;
35
+    this.misrouted = misrouted;
29 36
     this.legs = new ArrayList<LegDTO>();
30 37
   }
31 38
 
@@ -41,8 +48,8 @@ public final class CargoRoutingDTO implements Serializable {
41 48
     return finalDestination;
42 49
   }
43 50
 
44
-  public void addLeg(final String carrierMovementId, final String from, final String to) {
45
-    legs.add(new LegDTO(carrierMovementId, from, to));
51
+  public void addLeg(String voyageNumber, String from, String to, Date loadTime, Date unloadTime) {
52
+    legs.add(new LegDTO(voyageNumber, from, to, loadTime, unloadTime));
46 53
   }
47 54
 
48 55
   /**
@@ -52,8 +59,16 @@ public final class CargoRoutingDTO implements Serializable {
52 59
     return Collections.unmodifiableList(legs);
53 60
   }
54 61
 
62
+  public boolean isMisrouted() {
63
+    return misrouted;
64
+  }
65
+
55 66
   public boolean isRouted() {
56 67
     return !legs.isEmpty();
57 68
   }
58 69
 
70
+  public Date getArrivalDeadline() {
71
+    return arrivalDeadline;
72
+  }
73
+
59 74
 }

+ 16
- 1
dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/dto/LegDTO.java Просмотреть файл

@@ -1,6 +1,7 @@
1 1
 package se.citerus.dddsample.interfaces.booking.facade.dto;
2 2
 
3 3
 import java.io.Serializable;
4
+import java.util.Date;
4 5
 
5 6
 /**
6 7
  * DTO for a leg in an itinerary.
@@ -10,6 +11,8 @@ public final class LegDTO implements Serializable {
10 11
   private final String voyageNumber;
11 12
   private final String from;
12 13
   private final String to;
14
+  private final Date loadTime;
15
+  private final Date unloadTime;
13 16
 
14 17
   /**
15 18
    * Constructor.
@@ -17,11 +20,15 @@ public final class LegDTO implements Serializable {
17 20
    * @param voyageNumber
18 21
    * @param from
19 22
    * @param to
23
+   * @param loadTime
24
+   * @param unloadTime
20 25
    */
21
-  public LegDTO(final String voyageNumber, final String from, final String to) {
26
+  public LegDTO(final String voyageNumber, final String from, final String to, Date loadTime, Date unloadTime) {
22 27
     this.voyageNumber = voyageNumber;
23 28
     this.from = from;
24 29
     this.to = to;
30
+    this.loadTime = loadTime;
31
+    this.unloadTime = unloadTime;
25 32
   }
26 33
 
27 34
   public String getVoyageNumber() {
@@ -36,4 +43,12 @@ public final class LegDTO implements Serializable {
36 43
     return to;
37 44
   }
38 45
 
46
+  public Date getLoadTime() {
47
+    return loadTime;
48
+  }
49
+
50
+  public Date getUnloadTime() {
51
+    return unloadTime;
52
+  }
53
+  
39 54
 }

dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/dto/ItineraryCandidateDTO.java → dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/dto/RouteCandidateDTO.java Просмотреть файл

@@ -7,7 +7,7 @@ import java.util.List;
7 7
 /**
8 8
  * DTO for presenting and selecting an itinerary from a collection of candidates.
9 9
  */
10
-public final class ItineraryCandidateDTO implements Serializable {
10
+public final class RouteCandidateDTO implements Serializable {
11 11
 
12 12
   private final List<LegDTO> legs;
13 13
 
@@ -16,7 +16,7 @@ public final class ItineraryCandidateDTO implements Serializable {
16 16
    *
17 17
    * @param legs The legs for this itinerary.
18 18
    */
19
-  public ItineraryCandidateDTO(final List<LegDTO> legs) {
19
+  public RouteCandidateDTO(final List<LegDTO> legs) {
20 20
     this.legs = legs;
21 21
   }
22 22
 

+ 18
- 7
dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/internal/BookingServiceFacadeImpl.java Просмотреть файл

@@ -12,8 +12,8 @@ import se.citerus.dddsample.domain.model.location.LocationRepository;
12 12
 import se.citerus.dddsample.domain.model.location.UnLocode;
13 13
 import se.citerus.dddsample.interfaces.booking.facade.BookingServiceFacade;
14 14
 import se.citerus.dddsample.interfaces.booking.facade.dto.CargoRoutingDTO;
15
-import se.citerus.dddsample.interfaces.booking.facade.dto.ItineraryCandidateDTO;
16 15
 import se.citerus.dddsample.interfaces.booking.facade.dto.LocationDTO;
16
+import se.citerus.dddsample.interfaces.booking.facade.dto.RouteCandidateDTO;
17 17
 import se.citerus.dddsample.interfaces.booking.facade.internal.assembler.CargoRoutingDTOAssembler;
18 18
 import se.citerus.dddsample.interfaces.booking.facade.internal.assembler.ItineraryCandidateDTOAssembler;
19 19
 import se.citerus.dddsample.interfaces.booking.facade.internal.assembler.LocationDTOAssembler;
@@ -38,12 +38,14 @@ public class BookingServiceFacadeImpl implements BookingServiceFacade {
38 38
   private VoyageRepository voyageRepository;
39 39
   private final Logger logger = Logger.getLogger(BookingServiceFacadeImpl.class);
40 40
 
41
+  @Override
41 42
   public List<LocationDTO> listShippingLocations() {
42 43
     final List<Location> allLocations = locationRepository.findAll();
43 44
     final LocationDTOAssembler assembler = new LocationDTOAssembler();
44 45
     return assembler.toDTOList(allLocations);
45 46
   }
46 47
 
48
+  @Override
47 49
   public String bookNewCargo(String origin, String destination, Date arrivalDeadline) {
48 50
     TrackingId trackingId = bookingService.bookNewCargo(
49 51
       new UnLocode(origin), 
@@ -53,19 +55,27 @@ public class BookingServiceFacadeImpl implements BookingServiceFacade {
53 55
     return trackingId.idString();
54 56
   }
55 57
 
58
+  @Override
56 59
   public CargoRoutingDTO loadCargoForRouting(String trackingId) {
57 60
     final Cargo cargo = cargoRepository.find(new TrackingId(trackingId));
58 61
     final CargoRoutingDTOAssembler assembler = new CargoRoutingDTOAssembler();
59 62
     return assembler.toDTO(cargo);
60 63
   }
61 64
 
62
-  public void assignCargoToRoute(String trackingIdStr, ItineraryCandidateDTO itineraryCandidateDTO) {
63
-    final Itinerary itinerary = new ItineraryCandidateDTOAssembler().fromDTO(itineraryCandidateDTO, voyageRepository, locationRepository);
65
+  @Override
66
+  public void assignCargoToRoute(String trackingIdStr, RouteCandidateDTO routeCandidateDTO) {
67
+    final Itinerary itinerary = new ItineraryCandidateDTOAssembler().fromDTO(routeCandidateDTO, voyageRepository, locationRepository);
64 68
     final TrackingId trackingId = new TrackingId(trackingIdStr);
65 69
 
66 70
     bookingService.assignCargoToRoute(itinerary, trackingId);
67 71
   }
68 72
 
73
+  @Override
74
+  public void changeDestination(String trackingId, String destinationUnLocode) throws RemoteException {
75
+    bookingService.changeDestination(new TrackingId(trackingId), new UnLocode(destinationUnLocode));
76
+  }
77
+
78
+  @Override
69 79
   public List<CargoRoutingDTO> listAllCargos() {
70 80
     final List<Cargo> cargoList = cargoRepository.findAll();
71 81
     final List<CargoRoutingDTO> dtoList = new ArrayList<CargoRoutingDTO>(cargoList.size());
@@ -76,16 +86,17 @@ public class BookingServiceFacadeImpl implements BookingServiceFacade {
76 86
     return dtoList;
77 87
   }
78 88
 
79
-  public List<ItineraryCandidateDTO> requestPossibleRoutesForCargo(String trackingId) throws RemoteException {
89
+  @Override
90
+  public List<RouteCandidateDTO> requestPossibleRoutesForCargo(String trackingId) throws RemoteException {
80 91
     final List<Itinerary> itineraries = bookingService.requestPossibleRoutesForCargo(new TrackingId(trackingId));
81 92
 
82
-    final List<ItineraryCandidateDTO> itineraryCandidates = new ArrayList<ItineraryCandidateDTO>(itineraries.size());
93
+    final List<RouteCandidateDTO> routeCandidates = new ArrayList<RouteCandidateDTO>(itineraries.size());
83 94
     final ItineraryCandidateDTOAssembler dtoAssembler = new ItineraryCandidateDTOAssembler();
84 95
     for (Itinerary itinerary : itineraries) {
85
-      itineraryCandidates.add(dtoAssembler.toDTO(itinerary));
96
+      routeCandidates.add(dtoAssembler.toDTO(itinerary));
86 97
     }
87 98
 
88
-    return itineraryCandidates;
99
+    return routeCandidates;
89 100
   }
90 101
 
91 102
   public void setBookingService(BookingService bookingService) {

+ 7
- 4
dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/internal/assembler/CargoRoutingDTOAssembler.java Просмотреть файл

@@ -2,6 +2,7 @@ package se.citerus.dddsample.interfaces.booking.facade.internal.assembler;
2 2
 
3 3
 import se.citerus.dddsample.domain.model.cargo.Cargo;
4 4
 import se.citerus.dddsample.domain.model.cargo.Leg;
5
+import se.citerus.dddsample.domain.model.cargo.RoutingStatus;
5 6
 import se.citerus.dddsample.interfaces.booking.facade.dto.CargoRoutingDTO;
6 7
 
7 8
 /**
@@ -13,14 +14,16 @@ public class CargoRoutingDTOAssembler {
13 14
     final CargoRoutingDTO dto = new CargoRoutingDTO(
14 15
       cargo.trackingId().idString(),
15 16
       cargo.origin().unLocode().idString(),
16
-      cargo.routeSpecification().destination().unLocode().idString()
17
-    );
17
+      cargo.routeSpecification().destination().unLocode().idString(),
18
+      cargo.routeSpecification().arrivalDeadline(),
19
+      cargo.routingStatus().equals(RoutingStatus.MISROUTED));
18 20
     for (Leg leg : cargo.itinerary().legs()) {
19 21
       dto.addLeg(
20 22
         leg.voyage().voyageNumber().idString(),
21 23
         leg.loadLocation().unLocode().idString(),
22
-        leg.unloadLocation().unLocode().idString()
23
-      );
24
+        leg.unloadLocation().unLocode().idString(),
25
+        leg.loadTime(),
26
+        leg.unloadTime());
24 27
     }
25 28
     return dto;
26 29
   }

+ 7
- 7
dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/internal/assembler/ItineraryCandidateDTOAssembler.java Просмотреть файл

@@ -8,8 +8,8 @@ import se.citerus.dddsample.domain.model.carrier.VoyageRepository;
8 8
 import se.citerus.dddsample.domain.model.location.Location;
9 9
 import se.citerus.dddsample.domain.model.location.LocationRepository;
10 10
 import se.citerus.dddsample.domain.model.location.UnLocode;
11
-import se.citerus.dddsample.interfaces.booking.facade.dto.ItineraryCandidateDTO;
12 11
 import se.citerus.dddsample.interfaces.booking.facade.dto.LegDTO;
12
+import se.citerus.dddsample.interfaces.booking.facade.dto.RouteCandidateDTO;
13 13
 
14 14
 import java.util.ArrayList;
15 15
 import java.util.Date;
@@ -20,24 +20,24 @@ import java.util.List;
20 20
  */
21 21
 public class ItineraryCandidateDTOAssembler {
22 22
   
23
-  public ItineraryCandidateDTO toDTO(final Itinerary itinerary) {
23
+  public RouteCandidateDTO toDTO(final Itinerary itinerary) {
24 24
     final List<LegDTO> legDTOs = new ArrayList<LegDTO>(itinerary.legs().size());
25 25
     for (Leg leg : itinerary.legs()) {
26 26
       legDTOs.add(toLegDTO(leg));
27 27
     }
28
-    return new ItineraryCandidateDTO(legDTOs);
28
+    return new RouteCandidateDTO(legDTOs);
29 29
   }
30 30
 
31 31
   protected LegDTO toLegDTO(final Leg leg) {
32 32
     final VoyageNumber voyageNumber = leg.voyage().voyageNumber();
33 33
     final UnLocode from = leg.loadLocation().unLocode();
34 34
     final UnLocode to = leg.unloadLocation().unLocode();
35
-    return new LegDTO(voyageNumber.idString(), from.idString(), to.idString());
35
+    return new LegDTO(voyageNumber.idString(), from.idString(), to.idString(), leg.loadTime(), leg.unloadTime());
36 36
   }
37 37
 
38
-  public Itinerary fromDTO(ItineraryCandidateDTO itineraryCandidateDTO, VoyageRepository voyageRepository, LocationRepository locationRepository) {
39
-    final List<Leg> legs = new ArrayList<Leg>(itineraryCandidateDTO.getLegs().size());
40
-    for (LegDTO legDTO : itineraryCandidateDTO.getLegs()) {
38
+  public Itinerary fromDTO(RouteCandidateDTO routeCandidateDTO, VoyageRepository voyageRepository, LocationRepository locationRepository) {
39
+    final List<Leg> legs = new ArrayList<Leg>(routeCandidateDTO.getLegs().size());
40
+    for (LegDTO legDTO : routeCandidateDTO.getLegs()) {
41 41
       final VoyageNumber voyageNumber = new VoyageNumber(legDTO.getVoyageNumber());
42 42
       final Voyage voyage = voyageRepository.find(voyageNumber);
43 43
       final Location from = locationRepository.find(new UnLocode(legDTO.getFrom()));

+ 32
- 31
dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/web/CargoAdminController.java Просмотреть файл

@@ -3,9 +3,9 @@ package se.citerus.dddsample.interfaces.booking.web;
3 3
 import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
4 4
 import se.citerus.dddsample.interfaces.booking.facade.BookingServiceFacade;
5 5
 import se.citerus.dddsample.interfaces.booking.facade.dto.CargoRoutingDTO;
6
-import se.citerus.dddsample.interfaces.booking.facade.dto.ItineraryCandidateDTO;
7 6
 import se.citerus.dddsample.interfaces.booking.facade.dto.LegDTO;
8 7
 import se.citerus.dddsample.interfaces.booking.facade.dto.LocationDTO;
8
+import se.citerus.dddsample.interfaces.booking.facade.dto.RouteCandidateDTO;
9 9
 
10 10
 import javax.servlet.http.HttpServletRequest;
11 11
 import javax.servlet.http.HttpServletResponse;
@@ -28,71 +28,72 @@ public final class CargoAdminController extends MultiActionController {
28 28
 
29 29
   private BookingServiceFacade bookingServiceFacade;
30 30
 
31
-  public Map registrationForm(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
32
-    final Map<String, Object> map = new HashMap<String, Object>();
33
-    final List<LocationDTO> dtoList = bookingServiceFacade.listShippingLocations();
31
+  public Map registrationForm(HttpServletRequest request, HttpServletResponse response) throws Exception {
32
+    Map<String, Object> map = new HashMap<String, Object>();
33
+    List<LocationDTO> dtoList = bookingServiceFacade.listShippingLocations();
34 34
 
35
-    final List<String> unLocodeStrings = new ArrayList<String>();
35
+    List<String> unLocodeStrings = new ArrayList<String>();
36 36
 
37 37
     for (LocationDTO dto : dtoList) {
38 38
       unLocodeStrings.add(dto.getUnLocode());
39 39
     }
40 40
 
41 41
     map.put("unlocodes", unLocodeStrings);
42
+    map.put("locations", dtoList);
42 43
     return map;
43 44
   }
44 45
 
45
-  public void register(final HttpServletRequest request, final HttpServletResponse response,
46
-                       final RegistrationCommand command) throws Exception {
47
-    final Date arrivalDeadline = new SimpleDateFormat("M/dd/yyyy").parse(command.getArrivalDeadline());
48
-    final String trackingId = bookingServiceFacade.bookNewCargo(
46
+  public void register(HttpServletRequest request, HttpServletResponse response,
47
+                       RegistrationCommand command) throws Exception {
48
+    Date arrivalDeadline = new SimpleDateFormat("M/dd/yyyy").parse(command.getArrivalDeadline());
49
+    String trackingId = bookingServiceFacade.bookNewCargo(
49 50
       command.getOriginUnlocode(), command.getDestinationUnlocode(), arrivalDeadline
50 51
     );
51 52
     response.sendRedirect("show.html?trackingId=" + trackingId);
52 53
   }
53 54
 
54 55
   public Map list(HttpServletRequest request, HttpServletResponse response) throws Exception {
55
-    final Map<String, Object> map = new HashMap<String, Object>();
56
-    final List<CargoRoutingDTO> cargoList = bookingServiceFacade.listAllCargos();
56
+    Map<String, Object> map = new HashMap<String, Object>();
57
+    List<CargoRoutingDTO> cargoList = bookingServiceFacade.listAllCargos();
57 58
 
58 59
     map.put("cargoList", cargoList);
59 60
     return map;
60 61
   }
61 62
 
62
-  public Map show(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
63
-    final Map<String, Object> map = new HashMap<String, Object>();
64
-    final String trackingId = request.getParameter("trackingId");
65
-    final CargoRoutingDTO dto = bookingServiceFacade.loadCargoForRouting(trackingId);
63
+  public Map show(HttpServletRequest request, HttpServletResponse response) throws Exception {
64
+    Map<String, Object> map = new HashMap<String, Object>();
65
+    String trackingId = request.getParameter("trackingId");
66
+    CargoRoutingDTO dto = bookingServiceFacade.loadCargoForRouting(trackingId);
66 67
     map.put("cargo", dto);
67 68
     return map;
68 69
   }
69 70
 
70
-  public Map selectItinerary(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
71
-    final Map<String, Object> map = new HashMap<String, Object>();
72
-    final String trackingId = request.getParameter("trackingId");
73
-    final List<ItineraryCandidateDTO> itineraryCandidates = bookingServiceFacade.requestPossibleRoutesForCargo(trackingId);
74
-    map.put("itineraryCandidates", itineraryCandidates);
71
+  public Map selectItinerary(HttpServletRequest request, HttpServletResponse response) throws Exception {
72
+    Map<String, Object> map = new HashMap<String, Object>();
73
+    String trackingId = request.getParameter("trackingId");
75 74
 
75
+    List<RouteCandidateDTO> routeCandidates = bookingServiceFacade.requestPossibleRoutesForCargo(trackingId);
76
+    map.put("routeCandidates", routeCandidates);
77
+
78
+    CargoRoutingDTO cargoDTO = bookingServiceFacade.loadCargoForRouting(trackingId);
79
+    map.put("cargo", cargoDTO);
76 80
 
77
-    final CargoRoutingDTO cargoDTO = bookingServiceFacade.loadCargoForRouting(trackingId);
78
-    map.put("origin", cargoDTO.getOrigin());
79
-    map.put("destination", cargoDTO.getFinalDestination());
80
-    map.put("trackingId", trackingId);
81
-    
82 81
     return map;
83 82
   }
84 83
 
85
-  public void assignItinerary(final HttpServletRequest request, final HttpServletResponse response, RouteAssignmentCommand command) throws Exception {
86
-    final List<LegDTO> legDTOs = new ArrayList<LegDTO>(command.getLegs().size());
84
+  public void assignItinerary(HttpServletRequest request, HttpServletResponse response, RouteAssignmentCommand command) throws Exception {
85
+    List<LegDTO> legDTOs = new ArrayList<LegDTO>(command.getLegs().size());
87 86
     for (RouteAssignmentCommand.LegCommand leg : command.getLegs()) {
88
-      legDTOs.add(new LegDTO(leg.getVoyageNumber(), leg.getFromUnLocode(), leg.getToUnLocode()));
87
+      // TODO actual dates
88
+      legDTOs.add(new LegDTO(leg.getVoyageNumber(), leg.getFromUnLocode(), leg.getToUnLocode(), new Date(), new Date()));
89 89
     }
90 90
 
91
-    final ItineraryCandidateDTO selectedItinerary = new ItineraryCandidateDTO(legDTOs);
91
+    RouteCandidateDTO selectedRoute = new RouteCandidateDTO(legDTOs);
92 92
 
93
-    bookingServiceFacade.assignCargoToRoute(command.getTrackingId(), selectedItinerary);
93
+    bookingServiceFacade.assignCargoToRoute(command.getTrackingId(), selectedRoute);
94 94
 
95
-    response.sendRedirect("list.html");
95
+    response.sendRedirect("show.html?trackingId=" + command.getTrackingId());
96
+    //response.sendRedirect("list.html");
96 97
   }
97 98
 
98 99
   public void setBookingServiceFacade(BookingServiceFacade bookingServiceFacade) {

+ 42
- 7
dddsample/src/main/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingViewAdapter.java Просмотреть файл

@@ -8,10 +8,7 @@ import se.citerus.dddsample.domain.model.handling.HandlingEvent;
8 8
 import se.citerus.dddsample.domain.model.location.Location;
9 9
 
10 10
 import java.text.SimpleDateFormat;
11
-import java.util.ArrayList;
12
-import java.util.Collections;
13
-import java.util.List;
14
-import java.util.Locale;
11
+import java.util.*;
15 12
 
16 13
 /**
17 14
  * View adapter for displaying a cargo in a tracking context.
@@ -22,6 +19,7 @@ public final class CargoTrackingViewAdapter {
22 19
   private final MessageSource messageSource;
23 20
   private final Locale locale;
24 21
   private final List<HandlingEventViewAdapter> events;
22
+  private final String FORMAT = "yyyy-MM-dd hh:mm";
25 23
 
26 24
   /**
27 25
    * Constructor.
@@ -47,7 +45,7 @@ public final class CargoTrackingViewAdapter {
47 45
    * @return A formatted string for displaying the location.
48 46
    */
49 47
   private String getDisplayText(Location location) {
50
-    return location.unLocode().idString() + " (" + location.name() + ")";
48
+    return location.name();
51 49
   }
52 50
 
53 51
   /**
@@ -104,6 +102,13 @@ public final class CargoTrackingViewAdapter {
104 102
     return cargo.trackingId().idString();
105 103
   }
106 104
 
105
+  public String getEta() {
106
+    Date eta = cargo.estimatedTimeOfArrival();
107
+
108
+    if (eta == null) return "?";
109
+    else return new SimpleDateFormat(FORMAT).format(eta);
110
+  }
111
+
107 112
   /**
108 113
    * @return True if cargo is misdirected.
109 114
    */
@@ -117,7 +122,6 @@ public final class CargoTrackingViewAdapter {
117 122
   public final class HandlingEventViewAdapter {
118 123
 
119 124
     private final HandlingEvent handlingEvent;
120
-    private final String FORMAT = "yyyy-MM-dd hh:mm";
121 125
 
122 126
     /**
123 127
      * Constructor.
@@ -132,7 +136,7 @@ public final class CargoTrackingViewAdapter {
132 136
      * @return Location where the event occurred.
133 137
      */
134 138
     public String getLocation() {
135
-      return handlingEvent.location().unLocode().idString();
139
+      return handlingEvent.location().name();
136 140
     }
137 141
 
138 142
     /**
@@ -164,5 +168,36 @@ public final class CargoTrackingViewAdapter {
164 168
       return cargo.itinerary().isExpected(handlingEvent);
165 169
     }
166 170
 
171
+    public String getDescription() {
172
+      Object[] args;
173
+
174
+      switch (handlingEvent.type()) {
175
+        case LOAD:
176
+        case UNLOAD:
177
+          args = new Object[] {
178
+            handlingEvent.voyage().voyageNumber().idString(),
179
+            handlingEvent.location().name(),
180
+            handlingEvent.completionTime()
181
+          };
182
+          break;
183
+
184
+        case RECEIVE:
185
+        case CLAIM:
186
+          args = new Object[] {
187
+            handlingEvent.location().name(),
188
+            handlingEvent.completionTime()
189
+          };
190
+          break;
191
+
192
+        default:
193
+          args = new Object[] {};
194
+      }
195
+
196
+      String key = "deliveryHistory.eventDescription." + handlingEvent.type().name();
197
+
198
+      return messageSource.getMessage(key,args,locale);
199
+    }
200
+
167 201
   }
202
+  
168 203
 }

+ 9
- 1
dddsample/src/main/resources/messages_en.properties Просмотреть файл

@@ -2,4 +2,12 @@ cargo.status.NOT_RECEIVED=Not received
2 2
 cargo.status.IN_PORT=In port {0}
3 3
 cargo.status.ONBOARD_CARRIER=Onboard carrier {0}
4 4
 cargo.status.CLAIMED=Claimed
5
-cargo.status.UNKNOWN=Unknown
5
+cargo.status.UNKNOWN=Unknown
6
+
7
+deliveryHistory.eventDescription.NOT_RECEIVED=Cargo has not yet been received.
8
+
9
+deliveryHistory.eventDescription.LOAD=Loaded onto voyage {0} in {1}, at {2}.
10
+deliveryHistory.eventDescription.UNLOAD=Unloaded off voyage {0} in {1}, at {2}.
11
+deliveryHistory.eventDescription.RECEIVE=Received in {0}, at {1}.
12
+deliveryHistory.eventDescription.CLAIM=Claimed in {0}, at {1}.
13
+deliveryHistory.eventDescription.CUSTOMS=Cleared customs in {0}, at {1}.

+ 2
- 1
dddsample/src/main/resources/se/citerus/dddsample/infrastructure/persistence/hibernate/Cargo.hbm.xml Просмотреть файл

@@ -14,7 +14,8 @@
14 14
     <many-to-one name="origin" column="origin_id" not-null="false" cascade="none" update="false" foreign-key="origin_fk"/>
15 15
 
16 16
     <property name="misdirected" column="is_misdirected" not-null="true"/>
17
-
17
+    <property name="eta" column="eta" not-null="false"/>
18
+    
18 19
     <property name="routingStatus" column="routing_status" not-null="true">
19 20
       <type name="org.hibernate.type.EnumType">
20 21
         <param name="enumClass">se.citerus.dddsample.domain.model.cargo.RoutingStatus</param>

+ 5
- 8
dddsample/src/main/webapp/WEB-INF/jsp/admin/selectItinerary.jsp Просмотреть файл

@@ -12,21 +12,18 @@
12 12
 <body>
13 13
 <div id="container">
14 14
   <table>
15
-    <caption>Select route for cargo ${trackingId}</caption>
15
+    <caption>Select route</caption>
16 16
     <tr>
17
-      <td><strong>Origin:</strong></td>
18
-      <td>${origin}</td>
19
-    </tr>
20
-    <tr>
21
-      <td><strong>Destination:</strong></td>
22
-      <td>${destination}</td>
17
+      <td>
18
+          Cargo ${cargo.trackingId} is going from ${cargo.origin} to ${cargo.finalDestination}
19
+      </td>
23 20
     </tr>
24 21
   </table>
25 22
   <c:url value="/admin/assignItinerary.html" var="postUrl"/>
26 23
 
27 24
   <c:forEach items="${itineraryCandidates}" var="it" varStatus="itStatus">
28 25
       <form action="${postUrl}" method="post">
29
-        <input type="hidden" name="trackingId" value="${trackingId}"/>
26
+        <input type="hidden" name="trackingId" value="${cargo.trackingId}"/>
30 27
         <table>
31 28
           <caption>Route ${itStatus.index + 1}</caption>
32 29
           <thead>

+ 23
- 5
dddsample/src/main/webapp/WEB-INF/jsp/admin/show.jsp Просмотреть файл

@@ -13,19 +13,35 @@
13 13
       </tr>
14 14
       <tr>
15 15
         <td>Destination</td>
16
-        <td>${cargo.finalDestination}</td>
16
+        <td>
17
+            ${cargo.finalDestination}
18
+        </td>
19
+      </tr>
20
+      <tr>
21
+          <td></td>
22
+          <td>
23
+            <a href="">Change destination</a>    
24
+          </td>
25
+      </tr>
26
+      <tr>
27
+        <td>Arrival deadline</td>
28
+        <td><fmt:formatDate value="${cargo.arrivalDeadline}" pattern="yyyy-MM-dd hh:mm"/></td>
17 29
       </tr>
18 30
     </tbody>
19 31
   </table>
32
+  <p></p>
20 33
   <c:choose>
21 34
     <c:when test="${cargo.routed}">
22
-      <table>
35
+      <c:if test="${carg.misrouted}">
36
+      <p><em>Cargo is misrouted - <a href="${selectUrl}">reroute this cargo</a></em></p>    
37
+      </c:if>
38
+      <table border="1">
23 39
         <caption>Itinerary</caption>
24 40
         <thead>
25 41
           <tr>
26 42
             <td>Voyage number</td>
27
-            <td>From</td>
28
-            <td>To</td>
43
+            <td colspan="2">Load</td>
44
+            <td colspan="2">Unload</td>
29 45
           </tr>
30 46
         </thead>
31 47
         <tbody>
@@ -33,7 +49,9 @@
33 49
             <tr>
34 50
               <td>${leg.voyageNumber}</td>
35 51
               <td>${leg.from}</td>
52
+              <td>(<fmt:formatDate value="${leg.loadTime}" pattern="yyyy-MM-dd hh:mm"/>)</td>
36 53
               <td>${leg.to}</td>
54
+              <td>(<fmt:formatDate value="${leg.unloadTime}" pattern="yyyy-MM-dd hh:mm"/>)</td>
37 55
             </tr>
38 56
           </c:forEach>
39 57
         </tbody>
@@ -44,7 +62,7 @@
44 62
         <c:url value="/admin/selectItinerary.html" var="selectUrl">
45 63
           <c:param name="trackingId" value="${cargo.trackingId}"/>
46 64
         </c:url>
47
-        Not routed - <a href="${selectUrl}">Route this cargo</a>
65
+        <strong>Not routed</strong> - <a href="${selectUrl}">Route this cargo</a>
48 66
       </p>
49 67
     </c:otherwise>
50 68
   </c:choose>

+ 16
- 4
dddsample/src/main/webapp/WEB-INF/jsp/pub/track.jsp Просмотреть файл

@@ -4,13 +4,12 @@
4 4
 </head>
5 5
 <body>
6 6
 <div id="container">
7
-  <h1>Track Your Cargo</h1>
8 7
   <div id="search">
9 8
   <form:form method="post" commandName="trackCommand">
10 9
     <table>
11 10
       <tr>
12 11
         <td>
13
-          Enter tracking id:
12
+          Enter your tracking id:
14 13
         </td>
15 14
         <td>
16 15
           <form:input path="trackingId" id="idInput"/>
@@ -30,17 +29,19 @@
30 29
   </form:form>
31 30
   </div>
32 31
   <c:if test="${cargo == null}">
33
-    <p><em>Hint: try tracking "XYZ" or "JKL".</em></p>  
32
+    <p><em>Hint: try tracking "ABC123" or "JKL567".</em></p>
34 33
   </c:if>
35 34
 
36 35
   <c:if test="${cargo != null}">
37 36
     <div id="result">
38
-    <h2>Status: ${cargo.statusText}</h2>
37
+    <h2>Cargo ${cargo.trackingId} is now: ${cargo.statusText}</h2>
38
+    <p>Estimated time of arrival in ${cargo.destination}: ${cargo.eta}</p>
39 39
     <c:if test="${cargo.misdirected}">
40 40
       <p class="notify"><img src="${rc.contextPath}/images/error.png" alt="" />Cargo is misdirected</p>
41 41
     </c:if>
42 42
     <c:if test="${not empty cargo.events}">
43 43
       <h3>Delivery History</h3>
44
+      <%--
44 45
       <table cellspacing="4">
45 46
         <thead>
46 47
           <tr>
@@ -65,6 +66,17 @@
65 66
           </c:forEach>
66 67
         </tbody>
67 68
       </table>
69
+      --%>
70
+        <ul style="list-style-type: none;">
71
+            <c:forEach items="${cargo.events}" var="leg">
72
+            <li>
73
+                <p><img src="${rc.contextPath}/images/${leg.expected ? "tick" : "cross"}.png" alt=""/>
74
+                ${leg.description}</p>
75
+            </li>
76
+            </c:forEach>
77
+        </ul>
78
+
79
+
68 80
     </c:if>
69 81
   </div>
70 82
   </c:if>

+ 2
- 0
dddsample/src/main/webapp/WEB-INF/jspf/include.jspf Просмотреть файл

@@ -1,5 +1,7 @@
1 1
 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
2 2
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
3
+<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
3 4
 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
4 5
 <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
6
+
5 7
 <%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %>

+ 5
- 3
dddsample/src/main/webapp/style.css Просмотреть файл

@@ -7,7 +7,9 @@ body {
7 7
   font: 400 0.7em verdana, arial, sans-serif;
8 8
   line-height: 170%;
9 9
   color: #555;
10
-  background-color: orange;
10
+  background-color: #8599f7;
11
+  background-image: url("images/shade.png");
12
+  background-repeat: repeat-x;
11 13
 }
12 14
 
13 15
 /* Headers */
@@ -193,8 +195,8 @@ div#outer {
193 195
   height: 100%;
194 196
   text-align: center;
195 197
   background-color: white;
196
-  border-left: 5px dashed orange;
197
-  border-right: 5px dashed orange;
198
+  border-left: 2px solid #4223de;
199
+  border-right: 2px solid #4223de;
198 200
 }
199 201
 
200 202
 

+ 19
- 21
dddsample/src/test/java/se/citerus/dddsample/application/HandlingEventServiceTest.java Просмотреть файл

@@ -10,7 +10,6 @@ import se.citerus.dddsample.domain.model.cargo.TrackingId;
10 10
 import se.citerus.dddsample.domain.model.carrier.SampleVoyages;
11 11
 import se.citerus.dddsample.domain.model.carrier.VoyageNumber;
12 12
 import se.citerus.dddsample.domain.model.carrier.VoyageRepository;
13
-import se.citerus.dddsample.domain.model.handling.CannotCreateHandlingEventException;
14 13
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
15 14
 import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
16 15
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
@@ -62,9 +61,10 @@ public class HandlingEventServiceTest extends TestCase {
62 61
 
63 62
     replay(cargoRepository, voyageRepository, handlingEventRepository, locationRepository, applicationEvents);
64 63
 
65
-    service.registerHandlingEvent(new HandlingEventRegistrationAttempt(
64
+    final HandlingEventRegistrationAttempt attempt = new HandlingEventRegistrationAttempt(
66 65
       new Date(), new Date(), trackingId, voyageNumber, HandlingEvent.Type.LOAD, unLocode
67
-    ));
66
+    );
67
+    service.registerHandlingEvent(attempt.getCompletionTime(), attempt.getTrackingId(), attempt.getVoyageNumber(), attempt.getUnLocode(), attempt.getType());
68 68
   }
69 69
 
70 70
   public void testRegisterEventWithoutCarrierMovement() throws Exception {
@@ -78,9 +78,10 @@ public class HandlingEventServiceTest extends TestCase {
78 78
 
79 79
     replay(cargoRepository, voyageRepository, handlingEventRepository, locationRepository, applicationEvents);
80 80
 
81
-    service.registerHandlingEvent(new HandlingEventRegistrationAttempt(
81
+    final HandlingEventRegistrationAttempt attempt = new HandlingEventRegistrationAttempt(
82 82
       new Date(), new Date(), trackingId, null, HandlingEvent.Type.RECEIVE, STOCKHOLM.unLocode()
83
-    ));
83
+    );
84
+    service.registerHandlingEvent(attempt.getCompletionTime(), attempt.getTrackingId(), attempt.getVoyageNumber(), attempt.getUnLocode(), attempt.getType());
84 85
   }
85 86
   
86 87
 
@@ -93,13 +94,12 @@ public class HandlingEventServiceTest extends TestCase {
93 94
 
94 95
     expect(locationRepository.find(MELBOURNE.unLocode())).andReturn(MELBOURNE);
95 96
 
96
-    applicationEvents.rejectedHandlingEventRegistrationAttempt(isA(HandlingEventRegistrationAttempt.class), isA(CannotCreateHandlingEventException.class));
97
-
98 97
     replay(cargoRepository, voyageRepository, handlingEventRepository, locationRepository, applicationEvents);
99
-    
100
-    service.registerHandlingEvent(new HandlingEventRegistrationAttempt(
98
+
99
+    final HandlingEventRegistrationAttempt attempt = new HandlingEventRegistrationAttempt(
101 100
       new Date(), new Date(), trackingId, voyageNumber, HandlingEvent.Type.UNLOAD, MELBOURNE.unLocode()
102
-    ));
101
+    );
102
+    service.registerHandlingEvent(attempt.getCompletionTime(), attempt.getTrackingId(), attempt.getVoyageNumber(), attempt.getUnLocode(), attempt.getType());
103 103
   }
104 104
   
105 105
   public void testRegisterEventInvalidCargo() throws Exception {
@@ -108,13 +108,12 @@ public class HandlingEventServiceTest extends TestCase {
108 108
 
109 109
     expect(locationRepository.find(HONGKONG.unLocode())).andReturn(HONGKONG);
110 110
 
111
-    applicationEvents.rejectedHandlingEventRegistrationAttempt(isA(HandlingEventRegistrationAttempt.class), isA(CannotCreateHandlingEventException.class));
112
-
113 111
     replay(cargoRepository, voyageRepository, handlingEventRepository, locationRepository, applicationEvents);
114
-    
115
-      service.registerHandlingEvent(new HandlingEventRegistrationAttempt(
116
-        new Date(), new Date(), trackingId, new VoyageNumber("V001"), HandlingEvent.Type.CLAIM, HONGKONG.unLocode()
117
-      ));
112
+
113
+    final HandlingEventRegistrationAttempt attempt = new HandlingEventRegistrationAttempt(
114
+      new Date(), new Date(), trackingId, new VoyageNumber("V001"), HandlingEvent.Type.CLAIM, HONGKONG.unLocode()
115
+    );
116
+    service.registerHandlingEvent(attempt.getCompletionTime(), attempt.getTrackingId(), attempt.getVoyageNumber(), attempt.getUnLocode(), attempt.getType());
118 117
   }
119 118
   
120 119
   public void testRegisterEventInvalidLocation() throws Exception {
@@ -123,12 +122,11 @@ public class HandlingEventServiceTest extends TestCase {
123 122
     UnLocode wayOff = new UnLocode("XXYYY");
124 123
     expect(locationRepository.find(wayOff)).andReturn(null);
125 124
 
126
-    applicationEvents.rejectedHandlingEventRegistrationAttempt(isA(HandlingEventRegistrationAttempt.class), isA(CannotCreateHandlingEventException.class));
127
-
128 125
     replay(cargoRepository, voyageRepository, handlingEventRepository, locationRepository, applicationEvents);
129
-    
130
-    service.registerHandlingEvent(new HandlingEventRegistrationAttempt(
126
+
127
+    final HandlingEventRegistrationAttempt attempt = new HandlingEventRegistrationAttempt(
131 128
       new Date(), new Date(), trackingId, null, HandlingEvent.Type.CLAIM, wayOff
132
-    ));
129
+    );
130
+    service.registerHandlingEvent(attempt.getCompletionTime(), attempt.getTrackingId(), attempt.getVoyageNumber(), attempt.getUnLocode(), attempt.getType());
133 131
   }
134 132
 }

+ 1
- 1
dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/CargoTest.java Просмотреть файл

@@ -1,7 +1,7 @@
1 1
 package se.citerus.dddsample.domain.model.cargo;
2 2
 
3 3
 import junit.framework.TestCase;
4
-import se.citerus.dddsample.DateTestUtil;
4
+import se.citerus.dddsample.application.util.DateTestUtil;
5 5
 import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.*;
6 6
 import static se.citerus.dddsample.domain.model.cargo.TransportStatus.NOT_RECEIVED;
7 7
 import se.citerus.dddsample.domain.model.carrier.Voyage;

+ 1
- 1
dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/RouteSpecificationTest.java Просмотреть файл

@@ -1,7 +1,7 @@
1 1
 package se.citerus.dddsample.domain.model.cargo;
2 2
 
3 3
 import junit.framework.TestCase;
4
-import static se.citerus.dddsample.DateTestUtil.toDate;
4
+import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
5 5
 import se.citerus.dddsample.domain.model.carrier.Voyage;
6 6
 import se.citerus.dddsample.domain.model.carrier.VoyageNumber;
7 7
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;

+ 0
- 6
dddsample/src/test/java/se/citerus/dddsample/infrastructure/messaging/stub/SynchronousApplicationEventsStub.java Просмотреть файл

@@ -4,7 +4,6 @@ import se.citerus.dddsample.application.ApplicationEvents;
4 4
 import se.citerus.dddsample.application.CargoInspectionService;
5 5
 import se.citerus.dddsample.application.HandlingEventRegistrationAttempt;
6 6
 import se.citerus.dddsample.domain.model.cargo.Cargo;
7
-import se.citerus.dddsample.domain.model.handling.CannotCreateHandlingEventException;
8 7
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
9 8
 
10 9
 public class SynchronousApplicationEventsStub implements ApplicationEvents {
@@ -32,11 +31,6 @@ public class SynchronousApplicationEventsStub implements ApplicationEvents {
32 31
   }
33 32
 
34 33
   @Override
35
-  public void rejectedHandlingEventRegistrationAttempt(HandlingEventRegistrationAttempt attempt, CannotCreateHandlingEventException problem) {
36
-    System.out.println("EVENT: rejected handling event registration attempt: " + problem);
37
-  }
38
-
39
-  @Override
40 34
   public void receivedHandlingEventRegistrationAttempt(HandlingEventRegistrationAttempt attempt) {
41 35
     System.out.println("EVENT: received handling event registration attempt");
42 36
   }

+ 14
- 1
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/AbstractRepositoryTest.java Просмотреть файл

@@ -7,6 +7,8 @@ import org.springframework.orm.hibernate3.HibernateTransactionManager;
7 7
 import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
8 8
 import org.springframework.transaction.support.TransactionTemplate;
9 9
 import se.citerus.dddsample.application.util.SampleDataGenerator;
10
+import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
11
+import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
10 12
 
11 13
 import java.lang.reflect.Field;
12 14
 
@@ -14,6 +16,16 @@ public abstract class AbstractRepositoryTest extends AbstractTransactionalDataSo
14 16
 
15 17
   SessionFactory sessionFactory;
16 18
   SimpleJdbcTemplate sjt;
19
+  HandlingEventFactory handlingEventFactory;
20
+  HandlingEventRepository handlingEventRepository;
21
+
22
+  public void setHandlingEventFactory(HandlingEventFactory handlingEventFactory) {
23
+    this.handlingEventFactory = handlingEventFactory;
24
+  }
25
+
26
+  public void setHandlingEventRepository(HandlingEventRepository handlingEventRepository) {
27
+    this.handlingEventRepository = handlingEventRepository;
28
+  }
17 29
 
18 30
   protected AbstractRepositoryTest() {
19 31
     setAutowireMode(AUTOWIRE_BY_NAME);
@@ -35,11 +47,12 @@ public abstract class AbstractRepositoryTest extends AbstractTransactionalDataSo
35 47
 
36 48
   @Override
37 49
   protected String[] getConfigLocations() {
38
-    return new String[] {"/context-infrastructure-persistence.xml"};
50
+    return new String[] {"/context-infrastructure-persistence.xml", "context-domain.xml"};
39 51
   }
40 52
 
41 53
   @Override
42 54
   protected void onSetUpInTransaction() throws Exception {
55
+    // TODO store Sample* and object instances here instead of handwritten SQL
43 56
     SampleDataGenerator.loadSampleData(jdbcTemplate, new TransactionTemplate(transactionManager));
44 57
     sjt = new SimpleJdbcTemplate(jdbcTemplate);
45 58
   }

+ 0
- 6
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryTest.java Просмотреть файл

@@ -9,7 +9,6 @@ import se.citerus.dddsample.domain.model.carrier.VoyageRepository;
9 9
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
10 10
 import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.LOAD;
11 11
 import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.RECEIVE;
12
-import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
13 12
 import se.citerus.dddsample.domain.model.location.Location;
14 13
 import se.citerus.dddsample.domain.model.location.LocationRepository;
15 14
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
@@ -25,7 +24,6 @@ public class CargoRepositoryTest extends AbstractRepositoryTest {
25 24
   CargoRepository cargoRepository;
26 25
   LocationRepository locationRepository;
27 26
   VoyageRepository voyageRepository;
28
-  HandlingEventRepository handlingEventRepository;
29 27
 
30 28
   public void setCargoRepository(CargoRepository cargoRepository) {
31 29
     this.cargoRepository = cargoRepository;
@@ -39,10 +37,6 @@ public class CargoRepositoryTest extends AbstractRepositoryTest {
39 37
     this.voyageRepository = voyageRepository;
40 38
   }
41 39
 
42
-  public void setHandlingEventRepository(HandlingEventRepository handlingEventRepository) {
43
-    this.handlingEventRepository = handlingEventRepository;
44
-  }
45
-
46 40
   public void testFindByCargoId() {
47 41
     final TrackingId trackingId = new TrackingId("FGH");
48 42
     final Cargo cargo = cargoRepository.find(trackingId);

+ 5
- 5
dddsample/src/test/java/se/citerus/dddsample/interfaces/booking/facade/internal/assembler/ItineraryCandidateDTOAssemblerTest.java Просмотреть файл

@@ -11,8 +11,8 @@ import se.citerus.dddsample.domain.model.location.LocationRepository;
11 11
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
12 12
 import se.citerus.dddsample.domain.model.location.UnLocode;
13 13
 import se.citerus.dddsample.infrastructure.persistence.inmemory.VoyageRepositoryInMem;
14
-import se.citerus.dddsample.interfaces.booking.facade.dto.ItineraryCandidateDTO;
15 14
 import se.citerus.dddsample.interfaces.booking.facade.dto.LegDTO;
15
+import se.citerus.dddsample.interfaces.booking.facade.dto.RouteCandidateDTO;
16 16
 
17 17
 import java.util.ArrayList;
18 18
 import java.util.Arrays;
@@ -34,7 +34,7 @@ public class ItineraryCandidateDTOAssemblerTest extends TestCase {
34 34
       )
35 35
     );
36 36
 
37
-    final ItineraryCandidateDTO dto = assembler.toDTO(itinerary);
37
+    final RouteCandidateDTO dto = assembler.toDTO(itinerary);
38 38
 
39 39
     assertEquals(2, dto.getLegs().size());
40 40
     LegDTO legDTO = dto.getLegs().get(0);
@@ -52,8 +52,8 @@ public class ItineraryCandidateDTOAssemblerTest extends TestCase {
52 52
     final ItineraryCandidateDTOAssembler assembler = new ItineraryCandidateDTOAssembler();
53 53
 
54 54
     final List<LegDTO> legs = new ArrayList<LegDTO>();
55
-    legs.add(new LegDTO("CM001", "AAAAA", "BBBBB"));
56
-    legs.add(new LegDTO("CM001", "BBBBB", "CCCCC"));
55
+    legs.add(new LegDTO("CM001", "AAAAA", "BBBBB", new Date(), new Date()));
56
+    legs.add(new LegDTO("CM001", "BBBBB", "CCCCC", new Date(), new Date()));
57 57
 
58 58
     final LocationRepository locationRepository = createMock(LocationRepository.class);
59 59
     expect(locationRepository.find(new UnLocode("AAAAA"))).andReturn(HONGKONG);
@@ -66,7 +66,7 @@ public class ItineraryCandidateDTOAssemblerTest extends TestCase {
66 66
 
67 67
 
68 68
     // Tested call
69
-    final Itinerary itinerary = assembler.fromDTO(new ItineraryCandidateDTO(legs), voyageRepository, locationRepository);
69
+    final Itinerary itinerary = assembler.fromDTO(new RouteCandidateDTO(legs), voyageRepository, locationRepository);
70 70
 
71 71
     
72 72
     assertNotNull(itinerary);

+ 6
- 6
dddsample/src/test/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingViewAdapterTest.java Просмотреть файл

@@ -32,29 +32,29 @@ public class CargoTrackingViewAdapterTest extends TestCase {
32 32
     CargoTrackingViewAdapter adapter = new CargoTrackingViewAdapter(cargo, applicationContext, Locale.GERMAN, events);
33 33
 
34 34
     assertEquals("XYZ", adapter.getTrackingId());
35
-    assertEquals("CNHGH (Hangzhou)", adapter.getOrigin());
36
-    assertEquals("FIHEL (Helsinki)", adapter.getDestination());
37
-    assertEquals("In port FIHEL (Helsinki)", adapter.getStatusText());
35
+    assertEquals("Hangzhou", adapter.getOrigin());
36
+    assertEquals("Helsinki", adapter.getDestination());
37
+    assertEquals("In port Helsinki", adapter.getStatusText());
38 38
 
39 39
     Iterator<CargoTrackingViewAdapter.HandlingEventViewAdapter> it = adapter.getEvents().iterator();
40 40
 
41 41
     CargoTrackingViewAdapter.HandlingEventViewAdapter event = it.next();
42 42
     assertEquals("RECEIVE", event.getType());
43
-    assertEquals("CNHGH", event.getLocation());
43
+    assertEquals("Hangzhou", event.getLocation());
44 44
     assertEquals("1970-01-01 01:00", event.getTime());
45 45
     assertEquals("", event.getVoyageNumber());
46 46
     assertTrue(event.isExpected());
47 47
 
48 48
     event = it.next();
49 49
     assertEquals("LOAD", event.getType());
50
-    assertEquals("CNHGH", event.getLocation());
50
+    assertEquals("Hangzhou", event.getLocation());
51 51
     assertEquals("1970-01-01 01:00", event.getTime());
52 52
     assertEquals("CM001", event.getVoyageNumber());
53 53
     assertTrue(event.isExpected());
54 54
 
55 55
     event = it.next();
56 56
     assertEquals("UNLOAD", event.getType());
57
-    assertEquals("FIHEL", event.getLocation());
57
+    assertEquals("Helsinki", event.getLocation());
58 58
     assertEquals("1970-01-01 01:00", event.getTime());
59 59
     assertEquals("CM001", event.getVoyageNumber());
60 60
     assertTrue(event.isExpected());

+ 13
- 11
dddsample/src/test/java/se/citerus/dddsample/scenario/CargoLifecycleScenarioTest.java Просмотреть файл

@@ -135,12 +135,13 @@ public class CargoLifecycleScenarioTest extends TestCase {
135 135
     HandlingEventRegistrationAttempt attempt1 = new HandlingEventRegistrationAttempt(
136 136
       new Date(), new Date(100), trackingId, null, RECEIVE, HONGKONG.unLocode()
137 137
     );
138
-    handlingEventService.registerHandlingEvent(attempt1);
138
+    handlingEventService.registerHandlingEvent(attempt1.getCompletionTime(), attempt1.getTrackingId(), attempt1.getVoyageNumber(), attempt1.getUnLocode(), attempt1.getType());
139 139
 
140 140
     // Next event: Load onto voyage CM003 in Hongkong
141
-    handlingEventService.registerHandlingEvent(new HandlingEventRegistrationAttempt(
141
+    final HandlingEventRegistrationAttempt attempt = new HandlingEventRegistrationAttempt(
142 142
       new Date(), new Date(200), trackingId, CM003.voyageNumber(), LOAD, HONGKONG.unLocode()
143
-    ));
143
+    );
144
+    handlingEventService.registerHandlingEvent(attempt.getCompletionTime(), attempt.getTrackingId(), attempt.getVoyageNumber(), attempt.getUnLocode(), attempt.getType());
144 145
 
145 146
     // Check current state - should be ok
146 147
     assertEquals(CM003, cargo.delivery().currentVoyage());
@@ -160,13 +161,14 @@ public class CargoLifecycleScenarioTest extends TestCase {
160 161
     HandlingEventRegistrationAttempt failingAttempt = new HandlingEventRegistrationAttempt(
161 162
       new Date(), new Date(300), trackingId, noSuchVoyageNumber, LOAD, noSuchUnLocode
162 163
     );
163
-    handlingEventService.registerHandlingEvent(failingAttempt);
164
+    handlingEventService.registerHandlingEvent(failingAttempt.getCompletionTime(), failingAttempt.getTrackingId(), failingAttempt.getVoyageNumber(), failingAttempt.getUnLocode(), failingAttempt.getType());
164 165
 
165 166
 
166 167
     // Cargo is now (incorrectly) unloaded in Tokyo
167
-    handlingEventService.registerHandlingEvent(new HandlingEventRegistrationAttempt(
168
+    final HandlingEventRegistrationAttempt attempt7 = new HandlingEventRegistrationAttempt(
168 169
       new Date(), new Date(400), trackingId, CM003.voyageNumber(), UNLOAD, TOKYO.unLocode()
169
-    ));
170
+    );
171
+    handlingEventService.registerHandlingEvent(attempt7.getCompletionTime(), attempt7.getTrackingId(), attempt7.getVoyageNumber(), attempt7.getUnLocode(), attempt7.getType());
170 172
 
171 173
     // Check current state - cargo is misdirected!
172 174
     assertEquals(NONE, cargo.delivery().currentVoyage());
@@ -201,7 +203,7 @@ public class CargoLifecycleScenarioTest extends TestCase {
201 203
     HandlingEventRegistrationAttempt attempt2 = new HandlingEventRegistrationAttempt(
202 204
       new Date(), new Date(500), trackingId, CM003.voyageNumber(), LOAD, TOKYO.unLocode()
203 205
     );
204
-    handlingEventService.registerHandlingEvent(attempt2);
206
+    handlingEventService.registerHandlingEvent(attempt2.getCompletionTime(), attempt2.getTrackingId(), attempt2.getVoyageNumber(), attempt2.getUnLocode(), attempt2.getType());
205 207
 
206 208
     // Check current state - should be ok
207 209
     assertEquals(CM003, cargo.delivery().currentVoyage());
@@ -213,7 +215,7 @@ public class CargoLifecycleScenarioTest extends TestCase {
213 215
     HandlingEventRegistrationAttempt attempt3 = new HandlingEventRegistrationAttempt(
214 216
       new Date(), new Date(600), trackingId, CM003.voyageNumber(), UNLOAD, HAMBURG.unLocode()
215 217
     );
216
-    handlingEventService.registerHandlingEvent(attempt3);
218
+    handlingEventService.registerHandlingEvent(attempt3.getCompletionTime(), attempt3.getTrackingId(), attempt3.getVoyageNumber(), attempt3.getUnLocode(), attempt3.getType());
217 219
 
218 220
     // Check current state - should be ok
219 221
     assertEquals(NONE, cargo.delivery().currentVoyage());
@@ -225,7 +227,7 @@ public class CargoLifecycleScenarioTest extends TestCase {
225 227
     HandlingEventRegistrationAttempt attempt4 = new HandlingEventRegistrationAttempt(
226 228
       new Date(), new Date(700), trackingId, CM005.voyageNumber(), LOAD, HAMBURG.unLocode()
227 229
     );
228
-    handlingEventService.registerHandlingEvent(attempt4);
230
+    handlingEventService.registerHandlingEvent(attempt4.getCompletionTime(), attempt4.getTrackingId(), attempt4.getVoyageNumber(), attempt4.getUnLocode(), attempt4.getType());
229 231
 
230 232
     // Check current state - should be ok
231 233
     assertEquals(CM005, cargo.delivery().currentVoyage());
@@ -237,7 +239,7 @@ public class CargoLifecycleScenarioTest extends TestCase {
237 239
     HandlingEventRegistrationAttempt attempt5 = new HandlingEventRegistrationAttempt(
238 240
       new Date(), new Date(800), trackingId, CM005.voyageNumber(), UNLOAD, STOCKHOLM.unLocode()
239 241
     );
240
-    handlingEventService.registerHandlingEvent(attempt5);
242
+    handlingEventService.registerHandlingEvent(attempt5.getCompletionTime(), attempt5.getTrackingId(), attempt5.getVoyageNumber(), attempt5.getUnLocode(), attempt5.getType());
241 243
 
242 244
     // Check current state - should be ok
243 245
     assertEquals(NONE, cargo.delivery().currentVoyage());
@@ -249,7 +251,7 @@ public class CargoLifecycleScenarioTest extends TestCase {
249 251
     HandlingEventRegistrationAttempt attempt6 = new HandlingEventRegistrationAttempt(
250 252
       new Date(), new Date(900), trackingId, null, CLAIM, STOCKHOLM.unLocode()
251 253
     );
252
-    handlingEventService.registerHandlingEvent(attempt6);
254
+    handlingEventService.registerHandlingEvent(attempt6.getCompletionTime(), attempt6.getTrackingId(), attempt6.getVoyageNumber(), attempt6.getUnLocode(), attempt6.getType());
253 255
 
254 256
     // Check current state - should be ok
255 257
     assertEquals(NONE, cargo.delivery().currentVoyage());