Quellcode durchsuchen

Renamed DeliveryHistory to HandlingHistory.

Made next expected activity persistent and derived on handling.
peter_backlund vor 17 Jahren
Ursprung
Commit
7f48617bf4
22 geänderte Dateien mit 272 neuen und 176 gelöschten Zeilen
  1. 5
    1
      dddsample/src/main/java/se/citerus/dddsample/application/HandlingEventService.java
  2. 3
    5
      dddsample/src/main/java/se/citerus/dddsample/application/impl/CargoInspectionServiceImpl.java
  3. 5
    9
      dddsample/src/main/java/se/citerus/dddsample/application/util/SampleDataGenerator.java
  4. 53
    47
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Cargo.java
  5. 13
    27
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Delivery.java
  6. 4
    0
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/HandlingActivity.java
  7. 0
    11
      dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingEvent.java
  8. 2
    4
      dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingEventRepository.java
  9. 74
    0
      dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingHistory.java
  10. 2
    0
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryHibernate.java
  11. 7
    7
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryHibernate.java
  12. 1
    1
      dddsample/src/main/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingController.java
  13. 11
    0
      dddsample/src/main/resources/se/citerus/dddsample/infrastructure/persistence/hibernate/Cargo.hbm.xml
  14. 1
    29
      dddsample/src/main/webapp/WEB-INF/jsp/pub/track.jsp
  15. 15
    14
      dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/CargoTest.java
  16. 45
    0
      dddsample/src/test/java/se/citerus/dddsample/domain/model/handling/HandlingHistoryTest.java
  17. 1
    1
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryTest.java
  18. 2
    1
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryTest.java
  19. 7
    7
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/CargoRepositoryInMem.java
  20. 4
    4
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/HandlingEventRepositoryInMem.java
  21. 2
    1
      dddsample/src/test/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingViewAdapterTest.java
  22. 15
    7
      dddsample/src/test/java/se/citerus/dddsample/scenario/CargoLifecycleScenarioTest.java

+ 5
- 1
dddsample/src/main/java/se/citerus/dddsample/application/HandlingEventService.java Datei anzeigen

@@ -22,6 +22,10 @@ public interface HandlingEventService {
22 22
    * @param unLocode UN locode for the location where the event occurred
23 23
    * @param type type of event
24 24
    */
25
-  void registerHandlingEvent(Date completionTime, TrackingId trackingId, VoyageNumber voyageNumber, UnLocode unLocode, HandlingEvent.Type type);
25
+  void registerHandlingEvent(Date completionTime,
26
+                             TrackingId trackingId,
27
+                             VoyageNumber voyageNumber,
28
+                             UnLocode unLocode,
29
+                             HandlingEvent.Type type);
26 30
 
27 31
 }

+ 3
- 5
dddsample/src/main/java/se/citerus/dddsample/application/impl/CargoInspectionServiceImpl.java Datei anzeigen

@@ -9,10 +9,8 @@ import se.citerus.dddsample.application.CargoInspectionService;
9 9
 import se.citerus.dddsample.domain.model.cargo.Cargo;
10 10
 import se.citerus.dddsample.domain.model.cargo.CargoRepository;
11 11
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
12
-import se.citerus.dddsample.domain.model.handling.HandlingEvent;
13 12
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
14
-
15
-import java.util.List;
13
+import se.citerus.dddsample.domain.model.handling.HandlingHistory;
16 14
 
17 15
 public class CargoInspectionServiceImpl implements CargoInspectionService {
18 16
 
@@ -40,9 +38,9 @@ public class CargoInspectionServiceImpl implements CargoInspectionService {
40 38
       return;
41 39
     }
42 40
 
43
-    final List<HandlingEvent> handlingEvents = handlingEventRepository.findEventsForCargo(trackingId);
41
+    final HandlingHistory handlingHistory = handlingEventRepository.lookupHandlingHistoryOfCargo(trackingId);
44 42
 
45
-    cargo.deriveDeliveryProgress(handlingEvents);
43
+    cargo.deriveDeliveryProgress(handlingHistory);
46 44
 
47 45
     if (cargo.isMisdirected()) {
48 46
       applicationEvents.cargoWasMisdirected(cargo);

+ 5
- 9
dddsample/src/main/java/se/citerus/dddsample/application/util/SampleDataGenerator.java Datei anzeigen

@@ -12,10 +12,7 @@ import org.springframework.web.context.WebApplicationContext;
12 12
 import org.springframework.web.context.support.WebApplicationContextUtils;
13 13
 import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
14 14
 import se.citerus.dddsample.domain.model.cargo.*;
15
-import se.citerus.dddsample.domain.model.handling.CannotCreateHandlingEventException;
16
-import se.citerus.dddsample.domain.model.handling.HandlingEvent;
17
-import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
18
-import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
15
+import se.citerus.dddsample.domain.model.handling.*;
19 16
 import se.citerus.dddsample.domain.model.location.Location;
20 17
 import se.citerus.dddsample.domain.model.location.LocationRepository;
21 18
 import se.citerus.dddsample.domain.model.location.SampleLocations;
@@ -31,7 +28,6 @@ import java.text.ParseException;
31 28
 import java.text.SimpleDateFormat;
32 29
 import static java.util.Arrays.asList;
33 30
 import java.util.Date;
34
-import java.util.List;
35 31
 
36 32
 /**
37 33
  * Provides sample data.
@@ -254,8 +250,8 @@ public class SampleDataGenerator implements ServletContextListener {
254 250
           throw new RuntimeException(e);
255 251
         }
256 252
 
257
-        List<HandlingEvent> handlingEvents = handlingEventRepository.findEventsForCargo(trackingId);
258
-        abc123.deriveDeliveryProgress(handlingEvents);
253
+        HandlingHistory handlingHistory = handlingEventRepository.lookupHandlingHistoryOfCargo(trackingId);
254
+        abc123.deriveDeliveryProgress(handlingHistory);
259 255
 
260 256
         session.update(abc123);
261 257
 
@@ -299,8 +295,8 @@ public class SampleDataGenerator implements ServletContextListener {
299 295
           throw new RuntimeException(e);
300 296
         }
301 297
 
302
-        List<HandlingEvent> handlingEvents1 = handlingEventRepository.findEventsForCargo(trackingId1);
303
-        jkl567.deriveDeliveryProgress(handlingEvents1);
298
+        HandlingHistory handlingHistory1 = handlingEventRepository.lookupHandlingHistoryOfCargo(trackingId);
299
+        jkl567.deriveDeliveryProgress(handlingHistory1);
304 300
 
305 301
         session.update(jkl567);
306 302
       }

+ 53
- 47
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Cargo.java Datei anzeigen

@@ -5,13 +5,12 @@ import se.citerus.dddsample.domain.model.Entity;
5 5
 import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.*;
6 6
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
7 7
 import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.*;
8
+import se.citerus.dddsample.domain.model.handling.HandlingHistory;
8 9
 import se.citerus.dddsample.domain.model.location.Location;
9 10
 import se.citerus.dddsample.domain.shared.DomainObjectUtils;
10 11
 
11
-import java.util.Collections;
12 12
 import java.util.Date;
13 13
 import java.util.Iterator;
14
-import java.util.List;
15 14
 
16 15
 /**
17 16
  * A Cargo. This is the central class in the domain model,
@@ -49,6 +48,7 @@ public class Cargo implements Entity<Cargo> {
49 48
   private Delivery delivery;
50 49
   private RouteSpecification routeSpecification;
51 50
   private RoutingStatus routingStatus;
51
+  private HandlingActivity nextExpectedActivity;
52 52
   private boolean misdirected;
53 53
   private Date eta;
54 54
   
@@ -65,7 +65,7 @@ public class Cargo implements Entity<Cargo> {
65 65
     this.origin = routeSpecification.origin();
66 66
     this.routeSpecification = routeSpecification;
67 67
 
68
-    deriveDeliveryProgress(Collections.<HandlingEvent>emptyList());
68
+    deriveDeliveryProgress(HandlingHistory.EMPTY);
69 69
   }
70 70
 
71 71
   /**
@@ -130,6 +130,7 @@ public class Cargo implements Entity<Cargo> {
130 130
     // Handling consistency within the Cargo aggregate synchronously
131 131
     this.routingStatus = deriveRoutingStatus();
132 132
     this.misdirected = deriveMisdirectionStatus();
133
+    this.nextExpectedActivity = deriveNextExpectedActivity();
133 134
     this.eta = deriveEta();
134 135
   }
135 136
 
@@ -180,46 +181,7 @@ public class Cargo implements Entity<Cargo> {
180 181
    * @return the next expected activity
181 182
    */
182 183
   public HandlingActivity nextExpectedActivity() {
183
-    if (!onTrack()) return NO_ACTIVITY;
184
-
185
-    final HandlingEvent lastEvent = delivery().lastEvent();
186
-
187
-    if (lastEvent == null) return new HandlingActivity(RECEIVE, origin());
188
-
189
-    switch (lastEvent.type()) {
190
-
191
-      case LOAD:
192
-        for (Leg leg : itinerary().legs()) {
193
-          if (leg.loadLocation().sameIdentityAs(lastEvent.location())) {
194
-            return new HandlingActivity(UNLOAD, leg.unloadLocation(), leg.voyage());
195
-          }
196
-        }
197
-
198
-        return NO_ACTIVITY;
199
-
200
-      case UNLOAD:
201
-        for (Iterator<Leg> it = itinerary().legs().iterator(); it.hasNext();) {
202
-          final Leg leg = it.next();
203
-          if (leg.unloadLocation().sameIdentityAs(lastEvent.location())) {
204
-            if (it.hasNext()) {
205
-              final Leg nextLeg = it.next();
206
-              return new HandlingActivity(LOAD, nextLeg.loadLocation(), nextLeg.voyage());
207
-            } else {
208
-              return new HandlingActivity(CLAIM, leg.unloadLocation());
209
-            }
210
-          }
211
-        }
212
-
213
-        return NO_ACTIVITY;
214
-
215
-      case RECEIVE:
216
-        final Leg firstLeg = itinerary().legs().iterator().next();
217
-        return new HandlingActivity(LOAD, firstLeg.loadLocation(), firstLeg.voyage());
218
-
219
-      case CLAIM:
220
-      default:
221
-        return NO_ACTIVITY;
222
-    }
184
+    return nextExpectedActivity;
223 185
   }
224 186
 
225 187
   /**
@@ -235,15 +197,16 @@ public class Cargo implements Entity<Cargo> {
235 197
    * but changes to the delivery history (when a cargo is handled) cause the status update
236 198
    * to happen <b>asynchronously</b> since {@link HandlingEvent} is in a different aggregate.
237 199
    *
238
-   * @param handlingEvents all handling events for this cargo
200
+   * @param handlingHistory delivery history
239 201
    */
240
-  public void deriveDeliveryProgress(final List<HandlingEvent> handlingEvents) {
202
+  public void deriveDeliveryProgress(final HandlingHistory handlingHistory) {
241 203
     // Delivery is a value object, so we can simply discard the old one
242 204
     // and replace it with a new
243
-    this.delivery = Delivery.derivedFrom(handlingEvents);
205
+    this.delivery = Delivery.derivedFrom(handlingHistory);
244 206
     this.routingStatus = deriveRoutingStatus();
245 207
     this.misdirected = deriveMisdirectionStatus();
246 208
     this.eta = deriveEta();
209
+    this.nextExpectedActivity = deriveNextExpectedActivity();
247 210
   }
248 211
 
249 212
   /**
@@ -285,11 +248,54 @@ public class Cargo implements Entity<Cargo> {
285 248
     }
286 249
   }
287 250
 
251
+  private HandlingActivity deriveNextExpectedActivity() {
252
+    if (!onTrack()) return NO_ACTIVITY;
253
+
254
+    final HandlingEvent lastEvent = delivery().lastEvent();
255
+
256
+    if (lastEvent == null) return new HandlingActivity(RECEIVE, origin());
257
+
258
+    switch (lastEvent.type()) {
259
+
260
+      case LOAD:
261
+        for (Leg leg : itinerary().legs()) {
262
+          if (leg.loadLocation().sameIdentityAs(lastEvent.location())) {
263
+            return new HandlingActivity(UNLOAD, leg.unloadLocation(), leg.voyage());
264
+          }
265
+        }
266
+
267
+        return NO_ACTIVITY;
268
+
269
+      case UNLOAD:
270
+        for (Iterator<Leg> it = itinerary().legs().iterator(); it.hasNext();) {
271
+          final Leg leg = it.next();
272
+          if (leg.unloadLocation().sameIdentityAs(lastEvent.location())) {
273
+            if (it.hasNext()) {
274
+              final Leg nextLeg = it.next();
275
+              return new HandlingActivity(LOAD, nextLeg.loadLocation(), nextLeg.voyage());
276
+            } else {
277
+              return new HandlingActivity(CLAIM, leg.unloadLocation());
278
+            }
279
+          }
280
+        }
281
+
282
+        return NO_ACTIVITY;
283
+
284
+      case RECEIVE:
285
+        final Leg firstLeg = itinerary().legs().iterator().next();
286
+        return new HandlingActivity(LOAD, firstLeg.loadLocation(), firstLeg.voyage());
287
+
288
+      case CLAIM:
289
+      default:
290
+        return NO_ACTIVITY;
291
+    }
292
+  }
293
+
288 294
   /**
289 295
    * @return true if cargo is on track, i.e. routed and not misdirected
290 296
    */
291 297
   private boolean onTrack() {
292
-    return routingStatus().equals(ROUTED) && !misdirected;
298
+    return routingStatus.equals(ROUTED) && !misdirected;
293 299
   }
294 300
   
295 301
   @Override

+ 13
- 27
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Delivery.java Datei anzeigen

@@ -6,31 +6,29 @@ import org.apache.commons.lang.builder.HashCodeBuilder;
6 6
 import se.citerus.dddsample.domain.model.ValueObject;
7 7
 import static se.citerus.dddsample.domain.model.cargo.TransportStatus.*;
8 8
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
9
+import se.citerus.dddsample.domain.model.handling.HandlingHistory;
9 10
 import se.citerus.dddsample.domain.model.location.Location;
10 11
 import se.citerus.dddsample.domain.model.voyage.Voyage;
11 12
 import se.citerus.dddsample.domain.shared.DomainObjectUtils;
12 13
 
13
-import java.util.ArrayList;
14
-import java.util.Collection;
15
-import static java.util.Collections.EMPTY_SET;
16
-import static java.util.Collections.sort;
17
-import java.util.HashSet;
18
-import java.util.List;
19
-
20 14
 /**
21
- * The actual result of the cargo transportation, as opposed to
15
+ * The actual transportation of the cargo, as opposed to
22 16
  * the customer requirement (RouteSpecification) and the plan (Itinerary). 
23 17
  *
24 18
  */
25 19
 public class Delivery implements ValueObject<Delivery> {
26 20
 
27
-  public static final Delivery EMPTY_DELIVERY = Delivery.derivedFrom(EMPTY_SET);
21
+  public static final Delivery EMPTY_DELIVERY = Delivery.derivedFrom(HandlingHistory.EMPTY);
28 22
 
29 23
   private TransportStatus transportStatus;
30 24
   private Location lastKnownLocation;
31 25
   private Voyage currentVoyage;
32 26
   private HandlingEvent lastEvent;
33 27
 
28
+  private Delivery(HandlingEvent lastEvent) {
29
+    this.lastEvent = lastEvent;
30
+  }
31
+
34 32
   /**
35 33
    * @return Transport status
36 34
    */
@@ -53,19 +51,15 @@ public class Delivery implements ValueObject<Delivery> {
53 51
   }
54 52
 
55 53
   /**
56
-   * @param handlingEvents handling events
54
+   * @param handlingHistory delivery history
57 55
    * @return An up to date Delivery derived from this collection of handling events.
58 56
    */
59
-  static Delivery derivedFrom(final Collection<HandlingEvent> handlingEvents) {
60
-    Validate.notNull(handlingEvents, "Handling events are required");
57
+  static Delivery derivedFrom(HandlingHistory handlingHistory) {
58
+    Validate.notNull(handlingHistory, "Delivery history is required");
61 59
     
62
-    final List<HandlingEvent> eventsByCompletionTime =
63
-      new ArrayList<HandlingEvent>(
64
-        new HashSet<HandlingEvent>(handlingEvents));
65
-    sort(eventsByCompletionTime, HandlingEvent.BY_COMPLETION_TIME_COMPARATOR);
66
-
67
-    final Delivery delivery = new Delivery();
68
-    delivery.calculateLastEvent(eventsByCompletionTime);
60
+    final Delivery delivery = new Delivery(
61
+      handlingHistory.mostRecentlyCompletedEvent()
62
+    );
69 63
     delivery.calculateTransportStatus();
70 64
     delivery.calculateLastKnownLocation();
71 65
     delivery.calculateCurrentVoyage();
@@ -79,14 +73,6 @@ public class Delivery implements ValueObject<Delivery> {
79 73
     return lastEvent;
80 74
   }
81 75
 
82
-  private void calculateLastEvent(final List<HandlingEvent> handlingEvents) {
83
-    if (handlingEvents.isEmpty()) {
84
-      lastEvent =  null;
85
-    } else {
86
-      lastEvent = handlingEvents.get(handlingEvents.size() - 1);
87
-    }
88
-  }
89
-
90 76
   private void calculateTransportStatus() {
91 77
     if (lastEvent == null) {
92 78
       transportStatus = NOT_RECEIVED;

+ 4
- 0
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/HandlingActivity.java Datei anzeigen

@@ -81,4 +81,8 @@ public class HandlingActivity implements ValueObject<HandlingActivity> {
81 81
     return sameValueAs(other);
82 82
   }
83 83
 
84
+  HandlingActivity() {
85
+    // Needed by Hibernate
86
+  }
87
+  
84 88
 }

+ 0
- 11
dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingEvent.java Datei anzeigen

@@ -12,7 +12,6 @@ import se.citerus.dddsample.domain.model.location.Location;
12 12
 import se.citerus.dddsample.domain.model.voyage.Voyage;
13 13
 import se.citerus.dddsample.domain.shared.DomainObjectUtils;
14 14
 
15
-import java.util.Comparator;
16 15
 import java.util.Date;
17 16
 
18 17
 /**
@@ -33,16 +32,6 @@ import java.util.Date;
33 32
  */
34 33
 public final class HandlingEvent implements DomainEvent<HandlingEvent> {
35 34
 
36
-  /**
37
-   * Comparator used to be able to sort HandlingEvents according to their completion time
38
-   */
39
-  public static final Comparator<HandlingEvent> BY_COMPLETION_TIME_COMPARATOR =
40
-    new Comparator<HandlingEvent>() {
41
-      public int compare(final HandlingEvent he1, final HandlingEvent he2) {
42
-        return he1.completionTime().compareTo(he2.completionTime());
43
-      }
44
-    };
45
-
46 35
   private Type type;
47 36
   private Voyage voyage;
48 37
   private Location location;

+ 2
- 4
dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingEventRepository.java Datei anzeigen

@@ -2,8 +2,6 @@ package se.citerus.dddsample.domain.model.handling;
2 2
 
3 3
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
4 4
 
5
-import java.util.List;
6
-
7 5
 /**
8 6
  * Handling event repository.
9 7
  */
@@ -19,8 +17,8 @@ public interface HandlingEventRepository {
19 17
 
20 18
   /**
21 19
    * @param trackingId cargo tracking id
22
-   * @return All handling events for this cargo
20
+   * @return The handling history of this cargo
23 21
    */
24
-  List<HandlingEvent> findEventsForCargo(TrackingId trackingId);
22
+  HandlingHistory lookupHandlingHistoryOfCargo(TrackingId trackingId);
25 23
 
26 24
 }

+ 74
- 0
dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingHistory.java Datei anzeigen

@@ -0,0 +1,74 @@
1
+package se.citerus.dddsample.domain.model.handling;
2
+
3
+import org.apache.commons.lang.Validate;
4
+import se.citerus.dddsample.domain.model.ValueObject;
5
+
6
+import java.util.*;
7
+import static java.util.Collections.sort;
8
+
9
+/**
10
+ * The handling history of a cargo.
11
+ *
12
+ */
13
+public class HandlingHistory implements ValueObject<HandlingHistory> {
14
+
15
+  private final List<HandlingEvent> handlingEvents;
16
+
17
+  public static final HandlingHistory EMPTY = new HandlingHistory(Collections.<HandlingEvent>emptyList());
18
+
19
+  public HandlingHistory(Collection<HandlingEvent> handlingEvents) {
20
+    Validate.notNull(handlingEvents, "Handling events are required");
21
+
22
+    this.handlingEvents = new ArrayList<HandlingEvent>(handlingEvents);
23
+  }
24
+
25
+  /**
26
+   * @return A distinct list (no duplicate registrations) of handling events, ordered by completion time.
27
+   */
28
+  public List<HandlingEvent> distinctEventsByCompletionTime() {
29
+    final List<HandlingEvent> ordered = new ArrayList<HandlingEvent>(
30
+      new HashSet<HandlingEvent>(handlingEvents)
31
+    );
32
+    sort(ordered, BY_COMPLETION_TIME_COMPARATOR);
33
+    return Collections.unmodifiableList(ordered);
34
+  }
35
+
36
+  /**
37
+   * @return Most recently completed event, or null if the delivery history is empty.
38
+   */
39
+  public HandlingEvent mostRecentlyCompletedEvent() {
40
+    final List<HandlingEvent> distinctEvents = distinctEventsByCompletionTime();
41
+    if (distinctEvents.isEmpty()) {
42
+      return null;
43
+    } else {
44
+      return distinctEvents.get(distinctEvents.size() - 1);
45
+    }
46
+  }
47
+
48
+  @Override
49
+  public boolean sameValueAs(HandlingHistory other) {
50
+    return other != null && this.handlingEvents.equals(other.handlingEvents);
51
+  }
52
+
53
+  @Override
54
+  public boolean equals(Object o) {
55
+    if (this == o) return true;
56
+    if (o == null || getClass() != o.getClass()) return false;
57
+
58
+    final HandlingHistory other = (HandlingHistory) o;
59
+    return sameValueAs(other);
60
+  }
61
+
62
+  @Override
63
+  public int hashCode() {
64
+    return handlingEvents.hashCode();
65
+  }
66
+
67
+  private static final Comparator<HandlingEvent> BY_COMPLETION_TIME_COMPARATOR =
68
+    new Comparator<HandlingEvent>() {
69
+      public int compare(final HandlingEvent he1, final HandlingEvent he2) {
70
+        return he1.completionTime().compareTo(he2.completionTime());
71
+      }
72
+    };
73
+
74
+}

+ 2
- 0
dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryHibernate.java Datei anzeigen

@@ -23,10 +23,12 @@ public class CargoRepositoryHibernate extends HibernateRepository implements Car
23 23
 
24 24
   public void store(Cargo cargo) {
25 25
     getSession().saveOrUpdate(cargo);
26
+    // Delete-orphan does not seem to work correctly when the parent is a component
26 27
     getSession().createSQLQuery("delete from Leg where cargo_id = null").executeUpdate();
27 28
   }
28 29
 
29 30
   public TrackingId nextTrackingId() {
31
+    // TODO use an actual DB sequence here, UUID is for in-mem
30 32
     final String random = UUID.randomUUID().toString().toUpperCase();
31 33
     return new TrackingId(
32 34
       random.substring(0, random.indexOf("-"))

+ 7
- 7
dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryHibernate.java Datei anzeigen

@@ -4,8 +4,7 @@ import org.springframework.stereotype.Repository;
4 4
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
5 5
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
6 6
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
7
-
8
-import java.util.List;
7
+import se.citerus.dddsample.domain.model.handling.HandlingHistory;
9 8
 
10 9
 /**
11 10
  * Hibernate implementation of HandlingEventRepository.
@@ -15,16 +14,17 @@ import java.util.List;
15 14
 public class HandlingEventRepositoryHibernate extends HibernateRepository implements HandlingEventRepository {
16 15
 
17 16
   @Override
18
-  public void store(HandlingEvent event) {
17
+  public void store(final HandlingEvent event) {
19 18
     getSession().save(event);
20 19
   }
21 20
 
22 21
   @Override
23
-  public List<HandlingEvent> findEventsForCargo(TrackingId tid) {
24
-    return getSession().createQuery(
22
+  public HandlingHistory lookupHandlingHistoryOfCargo(final TrackingId trackingId) {
23
+    return new HandlingHistory(getSession().createQuery(
25 24
             "from HandlingEvent where cargo.trackingId = :tid").
26
-            setParameter("tid", tid).
27
-            list();
25
+            setParameter("tid", trackingId).
26
+            list()
27
+    );
28 28
   }
29 29
 
30 30
 }

+ 1
- 1
dddsample/src/main/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingController.java Datei anzeigen

@@ -56,7 +56,7 @@ public final class CargoTrackingController extends SimpleFormController {
56 56
     if (cargo != null) {
57 57
       final MessageSource messageSource = getApplicationContext();
58 58
       final Locale locale = RequestContextUtils.getLocale(request);
59
-      final List<HandlingEvent> handlingEvents = handlingEventRepository.findEventsForCargo(trackingId);
59
+      final List<HandlingEvent> handlingEvents = handlingEventRepository.lookupHandlingHistoryOfCargo(trackingId).distinctEventsByCompletionTime();
60 60
       model.put("cargo", new CargoTrackingViewAdapter(cargo, messageSource, locale, handlingEvents));
61 61
     } else {
62 62
       errors.rejectValue("trackingId", "cargo.unknown_id", new Object[]{trackCommand.getTrackingId()}, "Unknown tracking id");

+ 11
- 0
dddsample/src/main/resources/se/citerus/dddsample/infrastructure/persistence/hibernate/Cargo.hbm.xml Datei anzeigen

@@ -23,6 +23,17 @@
23 23
       </type>
24 24
     </property>
25 25
 
26
+    <component name="nextExpectedActivity" update="true">
27
+      <many-to-one name="location" column="next_expected_location_id" foreign-key="next_expected_location_fk"/>
28
+      <property name="type" column="next_expected_handling_event_type">
29
+        <type name="org.hibernate.type.EnumType">
30
+          <param name="enumClass">se.citerus.dddsample.domain.model.handling.HandlingEvent$Type</param>
31
+          <param name="type">12</param><!-- 12 is java.sql.Types.VARCHAR -->
32
+        </type>
33
+      </property>
34
+      <many-to-one name="voyage" column="next_expected_voyage_id" foreign-key="next_expected_voyage_fk"/>
35
+    </component>
36
+
26 37
     <component name="trackingId" unique="true" update="false">
27 38
       <property name="id" column="tracking_id"/>
28 39
     </component>

+ 1
- 29
dddsample/src/main/webapp/WEB-INF/jsp/pub/track.jsp Datei anzeigen

@@ -41,33 +41,7 @@
41 41
       <p class="notify"><img src="${rc.contextPath}/images/error.png" alt="" />Cargo is misdirected</p>
42 42
     </c:if>
43 43
     <c:if test="${not empty cargo.events}">
44
-      <h3>Delivery History</h3>
45
-      <%--
46
-      <table cellspacing="4">
47
-        <thead>
48
-          <tr>
49
-            <td>Event</td>
50
-            <td>Location</td>
51
-            <td>Time</td>
52
-            <td>Voyage number</td>
53
-            <td></td>
54
-          </tr>
55
-        </thead>
56
-        <tbody>
57
-          <c:forEach items="${cargo.events}" var="event">
58
-            <tr class="event-type-${event.type}">
59
-              <td>${event.type}</td>
60
-              <td>${event.location}</td>
61
-              <td>${event.time}</td>
62
-              <td>${event.voyageNumber}</td>
63
-              <td>
64
-                <img src="${rc.contextPath}/images/${event.expected ? "tick" : "cross"}.png" alt=""/>
65
-              </td>
66
-            </tr>
67
-          </c:forEach>
68
-        </tbody>
69
-      </table>
70
-      --%>
44
+      <h3>Handling History</h3>
71 45
         <ul style="list-style-type: none;">
72 46
             <c:forEach items="${cargo.events}" var="leg">
73 47
             <li>
@@ -76,8 +50,6 @@
76 50
             </li>
77 51
             </c:forEach>
78 52
         </ul>
79
-
80
-
81 53
     </c:if>
82 54
   </div>
83 55
   </c:if>

+ 15
- 14
dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/CargoTest.java Datei anzeigen

@@ -5,6 +5,7 @@ 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.handling.HandlingEvent;
8
+import se.citerus.dddsample.domain.model.handling.HandlingHistory;
8 9
 import se.citerus.dddsample.domain.model.location.Location;
9 10
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
10 11
 import se.citerus.dddsample.domain.model.voyage.Voyage;
@@ -118,7 +119,7 @@ public class CargoTest extends TestCase {
118 119
     // Adding an event unrelated to unloading at final destination
119 120
     events.add(
120 121
       new HandlingEvent(cargo, new Date(10), new Date(), HandlingEvent.Type.RECEIVE, HANGZOU));
121
-    cargo.deriveDeliveryProgress(events);
122
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
122 123
     assertFalse(cargo.isUnloadedAtDestination());
123 124
 
124 125
     Voyage voyage = new Voyage.Builder(new VoyageNumber("0123"), HANGZOU).
@@ -128,19 +129,19 @@ public class CargoTest extends TestCase {
128 129
     // Adding an unload event, but not at the final destination
129 130
     events.add(
130 131
       new HandlingEvent(cargo, new Date(20), new Date(), HandlingEvent.Type.UNLOAD, TOKYO, voyage));
131
-    cargo.deriveDeliveryProgress(events);
132
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
132 133
     assertFalse(cargo.isUnloadedAtDestination());
133 134
 
134 135
     // Adding an event in the final destination, but not unload
135 136
     events.add(
136 137
       new HandlingEvent(cargo, new Date(30), new Date(), HandlingEvent.Type.CUSTOMS, NEWYORK));
137
-    cargo.deriveDeliveryProgress(events);
138
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
138 139
     assertFalse(cargo.isUnloadedAtDestination());
139 140
 
140 141
     // Finally, cargo is unloaded at final destination
141 142
     events.add(
142 143
       new HandlingEvent(cargo, new Date(40), new Date(), HandlingEvent.Type.UNLOAD, NEWYORK, voyage));
143
-    cargo.deriveDeliveryProgress(events);
144
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
144 145
     assertTrue(cargo.isUnloadedAtDestination());
145 146
   }
146 147
 
@@ -150,7 +151,7 @@ public class CargoTest extends TestCase {
150 151
 
151 152
     HandlingEvent he = new HandlingEvent(cargo, getDate("2007-12-01"), new Date(), HandlingEvent.Type.RECEIVE, STOCKHOLM);
152 153
     events.add(he);
153
-    cargo.deriveDeliveryProgress(events);
154
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
154 155
 
155 156
     return cargo;
156 157
   }
@@ -159,7 +160,7 @@ public class CargoTest extends TestCase {
159 160
     final Cargo cargo = populateCargoOffMelbourne();
160 161
 
161 162
     events.add(new HandlingEvent(cargo, getDate("2007-12-09"), new Date(), HandlingEvent.Type.CLAIM, MELBOURNE));
162
-    cargo.deriveDeliveryProgress(events);
163
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
163 164
 
164 165
     return cargo;
165 166
   }
@@ -174,7 +175,7 @@ public class CargoTest extends TestCase {
174 175
     events.add(new HandlingEvent(cargo, getDate("2007-12-03"), new Date(), HandlingEvent.Type.LOAD, HAMBURG, voyage));
175 176
     events.add(new HandlingEvent(cargo, getDate("2007-12-04"), new Date(), HandlingEvent.Type.UNLOAD, HONGKONG, voyage));
176 177
 
177
-    cargo.deriveDeliveryProgress(events);
178
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
178 179
     return cargo;
179 180
   }
180 181
 
@@ -185,7 +186,7 @@ public class CargoTest extends TestCase {
185 186
     events.add(new HandlingEvent(cargo, getDate("2007-12-02"), new Date(), HandlingEvent.Type.UNLOAD, HAMBURG, voyage));
186 187
     events.add(new HandlingEvent(cargo, getDate("2007-12-03"), new Date(), HandlingEvent.Type.LOAD, HAMBURG, voyage));
187 188
 
188
-    cargo.deriveDeliveryProgress(events);
189
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
189 190
     return cargo;
190 191
   }
191 192
 
@@ -201,7 +202,7 @@ public class CargoTest extends TestCase {
201 202
     events.add(new HandlingEvent(cargo, getDate("2007-12-05"), new Date(), HandlingEvent.Type.LOAD, HONGKONG, voyage));
202 203
     events.add(new HandlingEvent(cargo, getDate("2007-12-07"), new Date(), HandlingEvent.Type.UNLOAD, MELBOURNE, voyage));
203 204
 
204
-    cargo.deriveDeliveryProgress(events);
205
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
205 206
     return cargo;
206 207
   }
207 208
 
@@ -216,7 +217,7 @@ public class CargoTest extends TestCase {
216 217
 
217 218
     events.add(new HandlingEvent(cargo, getDate("2007-12-05"), new Date(), HandlingEvent.Type.LOAD, HONGKONG, voyage));
218 219
 
219
-    cargo.deriveDeliveryProgress(events);
220
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
220 221
     return cargo;
221 222
   }
222 223
 
@@ -242,7 +243,7 @@ public class CargoTest extends TestCase {
242 243
     handlingEvents.add(new HandlingEvent(cargo, new Date(130), new Date(140), HandlingEvent.Type.CUSTOMS, GOTHENBURG));
243 244
 
244 245
     events.addAll(handlingEvents);
245
-    cargo.deriveDeliveryProgress(events);
246
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
246 247
     assertFalse(cargo.isMisdirected());
247 248
 
248 249
     //Try a couple of failing ones
@@ -252,7 +253,7 @@ public class CargoTest extends TestCase {
252 253
 
253 254
     handlingEvents.add(new HandlingEvent(cargo, new Date(), new Date(), HandlingEvent.Type.RECEIVE, HANGZOU));
254 255
     events.addAll(handlingEvents);
255
-    cargo.deriveDeliveryProgress(events);
256
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
256 257
 
257 258
     assertTrue(cargo.isMisdirected());
258 259
 
@@ -266,7 +267,7 @@ public class CargoTest extends TestCase {
266 267
     handlingEvents.add(new HandlingEvent(cargo, new Date(70), new Date(80), HandlingEvent.Type.LOAD, ROTTERDAM, voyage));
267 268
 
268 269
     events.addAll(handlingEvents);
269
-    cargo.deriveDeliveryProgress(events);
270
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
270 271
 
271 272
     assertTrue(cargo.isMisdirected());
272 273
 
@@ -280,7 +281,7 @@ public class CargoTest extends TestCase {
280 281
     handlingEvents.add(new HandlingEvent(cargo, new Date(), new Date(), HandlingEvent.Type.CLAIM, ROTTERDAM));
281 282
 
282 283
     events.addAll(handlingEvents);
283
-    cargo.deriveDeliveryProgress(events);
284
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
284 285
 
285 286
     assertTrue(cargo.isMisdirected());
286 287
   }

+ 45
- 0
dddsample/src/test/java/se/citerus/dddsample/domain/model/handling/HandlingHistoryTest.java Datei anzeigen

@@ -0,0 +1,45 @@
1
+package se.citerus.dddsample.domain.model.handling;
2
+
3
+import junit.framework.TestCase;
4
+import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
5
+import se.citerus.dddsample.domain.model.cargo.Cargo;
6
+import se.citerus.dddsample.domain.model.cargo.RouteSpecification;
7
+import se.citerus.dddsample.domain.model.cargo.TrackingId;
8
+import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
9
+import se.citerus.dddsample.domain.model.voyage.Voyage;
10
+import se.citerus.dddsample.domain.model.voyage.VoyageNumber;
11
+
12
+import static java.util.Arrays.asList;
13
+import java.util.Date;
14
+
15
+
16
+public class HandlingHistoryTest extends TestCase {
17
+  Cargo cargo;
18
+  Voyage voyage;
19
+  HandlingEvent event1;
20
+  HandlingEvent event1duplicate;
21
+  HandlingEvent event2;
22
+  HandlingHistory handlingHistory;
23
+
24
+  protected void setUp() throws Exception {
25
+    cargo = new Cargo(new TrackingId("ABC"), new RouteSpecification(SHANGHAI, DALLAS, toDate("2009-04-01")));
26
+    voyage = new Voyage.Builder(new VoyageNumber("X25"), HONGKONG).
27
+      addMovement(SHANGHAI, new Date(), new Date()).
28
+      addMovement(DALLAS, new Date(), new Date()).
29
+      build();
30
+    event1 = new HandlingEvent(cargo, toDate("2009-03-05"), new Date(100), HandlingEvent.Type.LOAD, SHANGHAI, voyage);
31
+    event1duplicate = new HandlingEvent(cargo, toDate("2009-03-05"), new Date(200), HandlingEvent.Type.LOAD, SHANGHAI, voyage);
32
+    event2 = new HandlingEvent(cargo, toDate("2009-03-10"), new Date(150), HandlingEvent.Type.UNLOAD, DALLAS, voyage);
33
+
34
+    handlingHistory = new HandlingHistory(asList(event2, event1, event1duplicate));
35
+  }
36
+
37
+  public void testDistinctEventsByCompletionTime() {
38
+    assertEquals(asList(event1, event2), handlingHistory.distinctEventsByCompletionTime());
39
+  }
40
+
41
+  public void testMostRecentlyCompletedEvent() {
42
+    assertEquals(event2, handlingHistory.mostRecentlyCompletedEvent());
43
+  }
44
+  
45
+}

+ 1
- 1
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryTest.java Datei anzeigen

@@ -46,7 +46,7 @@ public class CargoRepositoryTest extends AbstractRepositoryTest {
46 46
 
47 47
     assertNotNull(cargo.delivery());
48 48
 
49
-    final List<HandlingEvent> events = handlingEventRepository.findEventsForCargo(trackingId);
49
+    final List<HandlingEvent> events = handlingEventRepository.lookupHandlingHistoryOfCargo(trackingId).distinctEventsByCompletionTime();
50 50
     assertEquals(2, events.size());
51 51
 
52 52
     HandlingEvent firstEvent = events.get(0);

+ 2
- 1
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryTest.java Datei anzeigen

@@ -52,7 +52,8 @@ public class HandlingEventRepositoryTest extends AbstractRepositoryTest {
52 52
   }
53 53
 
54 54
   public void testFindEventsForCargo() throws Exception {
55
-    List<HandlingEvent> handlingEvents = handlingEventRepository.findEventsForCargo(new TrackingId("XYZ"));
55
+    TrackingId trackingId = new TrackingId("XYZ");
56
+    List<HandlingEvent> handlingEvents = handlingEventRepository.lookupHandlingHistoryOfCargo(trackingId).distinctEventsByCompletionTime();
56 57
     assertEquals(12, handlingEvents.size());
57 58
   }
58 59
 

+ 7
- 7
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/CargoRepositoryInMem.java Datei anzeigen

@@ -5,8 +5,8 @@ import se.citerus.dddsample.domain.model.cargo.Cargo;
5 5
 import se.citerus.dddsample.domain.model.cargo.CargoRepository;
6 6
 import se.citerus.dddsample.domain.model.cargo.RouteSpecification;
7 7
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
8
-import se.citerus.dddsample.domain.model.handling.HandlingEvent;
9 8
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
9
+import se.citerus.dddsample.domain.model.handling.HandlingHistory;
10 10
 import se.citerus.dddsample.domain.model.location.Location;
11 11
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
12 12
 
@@ -59,22 +59,22 @@ public class CargoRepositoryInMem implements CargoRepository {
59 59
   public void init() throws Exception {
60 60
     final TrackingId xyz = new TrackingId("XYZ");
61 61
     final Cargo cargoXYZ = createCargoWithDeliveryHistory(
62
-      xyz, STOCKHOLM, MELBOURNE, handlingEventRepository.findEventsForCargo(xyz));
62
+      xyz, STOCKHOLM, MELBOURNE, handlingEventRepository.lookupHandlingHistoryOfCargo(xyz));
63 63
     cargoDb.put(xyz.idString(), cargoXYZ);
64 64
 
65 65
     final TrackingId zyx = new TrackingId("ZYX");
66 66
     final Cargo cargoZYX = createCargoWithDeliveryHistory(
67
-      zyx, MELBOURNE, STOCKHOLM, handlingEventRepository.findEventsForCargo(zyx));
67
+      zyx, MELBOURNE, STOCKHOLM, handlingEventRepository.lookupHandlingHistoryOfCargo(zyx));
68 68
     cargoDb.put(zyx.idString(), cargoZYX);
69 69
 
70 70
     final TrackingId abc = new TrackingId("ABC");
71 71
     final Cargo cargoABC = createCargoWithDeliveryHistory(
72
-      abc, STOCKHOLM, HELSINKI, handlingEventRepository.findEventsForCargo(abc));
72
+      abc, STOCKHOLM, HELSINKI, handlingEventRepository.lookupHandlingHistoryOfCargo(abc));
73 73
     cargoDb.put(abc.idString(), cargoABC);
74 74
 
75 75
     final TrackingId cba = new TrackingId("CBA");
76 76
     final Cargo cargoCBA = createCargoWithDeliveryHistory(
77
-      cba, HELSINKI, STOCKHOLM, handlingEventRepository.findEventsForCargo(cba));
77
+      cba, HELSINKI, STOCKHOLM, handlingEventRepository.lookupHandlingHistoryOfCargo(cba));
78 78
     cargoDb.put(cba.idString(), cargoCBA);
79 79
   }
80 80
 
@@ -85,11 +85,11 @@ public class CargoRepositoryInMem implements CargoRepository {
85 85
   public static Cargo createCargoWithDeliveryHistory(TrackingId trackingId,
86 86
                                                      Location origin,
87 87
                                                      Location destination,
88
-                                                     Collection<HandlingEvent> events) {
88
+                                                     HandlingHistory handlingHistory) {
89 89
 
90 90
     final RouteSpecification routeSpecification = new RouteSpecification(origin, destination, new Date());
91 91
     final Cargo cargo = new Cargo(trackingId, routeSpecification);
92
-    cargo.deriveDeliveryProgress(new ArrayList<HandlingEvent>(events));
92
+    cargo.deriveDeliveryProgress(handlingHistory);
93 93
 
94 94
     return cargo;
95 95
   }

+ 4
- 4
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/HandlingEventRepositoryInMem.java Datei anzeigen

@@ -6,16 +6,15 @@ import se.citerus.dddsample.domain.model.cargo.Cargo;
6 6
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
7 7
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
8 8
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
9
+import se.citerus.dddsample.domain.model.handling.HandlingHistory;
9 10
 import se.citerus.dddsample.domain.model.voyage.CarrierMovement;
10 11
 import se.citerus.dddsample.domain.model.voyage.VoyageRepository;
11 12
 
12 13
 import java.text.DateFormat;
13 14
 import java.text.ParseException;
14 15
 import java.text.SimpleDateFormat;
15
-import java.util.ArrayList;
16 16
 import java.util.Date;
17 17
 import java.util.HashMap;
18
-import java.util.List;
19 18
 
20 19
 public class HandlingEventRepositoryInMem implements HandlingEventRepository {
21 20
   private final Log logger = LogFactory.getLog(getClass());
@@ -86,8 +85,9 @@ public class HandlingEventRepositoryInMem implements HandlingEventRepository {
86 85
     eventDB.put(event.cargo().trackingId().idString(), event);
87 86
   }
88 87
 
89
-  public List<HandlingEvent> findEventsForCargo(TrackingId trackingId) {
90
-    return new ArrayList();
88
+  @Override
89
+  public HandlingHistory lookupHandlingHistoryOfCargo(TrackingId trackingId) {
90
+    return HandlingHistory.EMPTY;
91 91
   }
92 92
 
93 93
   /**

+ 2
- 1
dddsample/src/test/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingViewAdapterTest.java Datei anzeigen

@@ -6,6 +6,7 @@ import se.citerus.dddsample.domain.model.cargo.Cargo;
6 6
 import se.citerus.dddsample.domain.model.cargo.RouteSpecification;
7 7
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
8 8
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
9
+import se.citerus.dddsample.domain.model.handling.HandlingHistory;
9 10
 import static se.citerus.dddsample.domain.model.location.SampleLocations.HANGZOU;
10 11
 import static se.citerus.dddsample.domain.model.location.SampleLocations.HELSINKI;
11 12
 import static se.citerus.dddsample.domain.model.voyage.SampleVoyages.CM001;
@@ -23,7 +24,7 @@ public class CargoTrackingViewAdapterTest extends TestCase {
23 24
     events.add(new HandlingEvent(cargo, new Date(3), new Date(4), HandlingEvent.Type.LOAD, HANGZOU, CM001));
24 25
     events.add(new HandlingEvent(cargo, new Date(5), new Date(6), HandlingEvent.Type.UNLOAD, HELSINKI, CM001));
25 26
 
26
-    cargo.deriveDeliveryProgress(events);
27
+    cargo.deriveDeliveryProgress(new HandlingHistory(events));
27 28
 
28 29
     StaticApplicationContext applicationContext = new StaticApplicationContext();
29 30
     applicationContext.addMessage("cargo.status.IN_PORT", Locale.GERMAN, "In port {0}");

+ 15
- 7
dddsample/src/test/java/se/citerus/dddsample/scenario/CargoLifecycleScenarioTest.java Datei anzeigen

@@ -9,11 +9,13 @@ import se.citerus.dddsample.application.impl.BookingServiceImpl;
9 9
 import se.citerus.dddsample.application.impl.CargoInspectionServiceImpl;
10 10
 import se.citerus.dddsample.application.impl.HandlingEventServiceImpl;
11 11
 import se.citerus.dddsample.domain.model.cargo.*;
12
+import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.*;
12 13
 import static se.citerus.dddsample.domain.model.cargo.TransportStatus.*;
13 14
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
14 15
 import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.*;
15 16
 import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
16 17
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
18
+import se.citerus.dddsample.domain.model.handling.HandlingHistory;
17 19
 import se.citerus.dddsample.domain.model.location.Location;
18 20
 import se.citerus.dddsample.domain.model.location.LocationRepository;
19 21
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
@@ -103,7 +105,7 @@ public class CargoLifecycleScenarioTest extends TestCase {
103 105
     Cargo cargo = cargoRepository.find(trackingId);
104 106
     assertNotNull(cargo);
105 107
     assertEquals(NOT_RECEIVED, cargo.delivery().transportStatus());
106
-    assertEquals(RoutingStatus.NOT_ROUTED, cargo.routingStatus());
108
+    assertEquals(NOT_ROUTED, cargo.routingStatus());
107 109
     assertFalse(cargo.isMisdirected());
108 110
     assertNull(cargo.estimatedTimeOfArrival());
109 111
     assertNull(cargo.nextExpectedActivity());
@@ -120,7 +122,8 @@ public class CargoLifecycleScenarioTest extends TestCase {
120 122
     Itinerary itinerary = selectPreferedItinerary(itineraries);
121 123
     cargo.assignToRoute(itinerary);
122 124
 
123
-    assertEquals(RoutingStatus.ROUTED, cargo.routingStatus());
125
+    assertEquals(NOT_RECEIVED, cargo.delivery().transportStatus());
126
+    assertEquals(ROUTED, cargo.routingStatus());
124 127
     assertNotNull(cargo.estimatedTimeOfArrival());
125 128
     assertEquals(new HandlingActivity(RECEIVE, HONGKONG), cargo.nextExpectedActivity());
126 129
 
@@ -142,6 +145,9 @@ public class CargoLifecycleScenarioTest extends TestCase {
142 145
       new Date(100), trackingId, null, HONGKONG.unLocode(), RECEIVE
143 146
     );
144 147
 
148
+    assertEquals(IN_PORT, cargo.delivery().transportStatus());
149
+    assertEquals(HONGKONG, cargo.delivery().lastKnownLocation());
150
+    
145 151
     // Next event: Load onto voyage CM003 in Hongkong
146 152
     handlingEventService.registerHandlingEvent(
147 153
       new Date(200), trackingId, CM003.voyageNumber(), HONGKONG.unLocode(), LOAD
@@ -160,7 +166,7 @@ public class CargoLifecycleScenarioTest extends TestCase {
160 166
       because there is no voyage with the specified voyage number,
161 167
       and there's no location with the specified UN Locode either.
162 168
 
163
-      This attempt will be rejected and will not affet the cargo delivery in any way.
169
+      This attempt will be rejected and will not affect the cargo delivery in any way.
164 170
      */
165 171
     final VoyageNumber noSuchVoyageNumber = new VoyageNumber("XX000");
166 172
     final UnLocode noSuchUnLocode = new UnLocode("ZZZZZ");
@@ -190,7 +196,7 @@ public class CargoLifecycleScenarioTest extends TestCase {
190 196
     cargo.specifyNewRoute(fromTokyo);
191 197
 
192 198
     // The old itinerary does not satisfy the new specification
193
-    assertEquals(RoutingStatus.MISROUTED, cargo.routingStatus());
199
+    assertEquals(MISROUTED, cargo.routingStatus());
194 200
     assertNull(cargo.nextExpectedActivity());
195 201
 
196 202
     // Repeat procedure of selecting one out of a number of possible routes satisfying the route spec
@@ -199,7 +205,7 @@ public class CargoLifecycleScenarioTest extends TestCase {
199 205
     cargo.assignToRoute(newItinerary);
200 206
 
201 207
     // New itinerary should satisfy new route
202
-    assertEquals(RoutingStatus.ROUTED, cargo.routingStatus());
208
+    assertEquals(ROUTED, cargo.routingStatus());
203 209
 
204 210
     // TODO we can't handle the face that after a reroute, the cargo isn't misdirected anymore
205 211
     //assertFalse(cargo.isMisdirected());
@@ -323,6 +329,7 @@ public class CargoLifecycleScenarioTest extends TestCase {
323 329
     handlingEventRepository = new HandlingEventRepository() {
324 330
       Map<TrackingId, List<HandlingEvent>> eventMap = new HashMap<TrackingId, List<HandlingEvent>>();
325 331
 
332
+      @Override
326 333
       public void store(HandlingEvent event) {
327 334
         final TrackingId trackingId = event.cargo().trackingId();
328 335
         List<HandlingEvent> list = eventMap.get(trackingId);
@@ -333,8 +340,9 @@ public class CargoLifecycleScenarioTest extends TestCase {
333 340
         list.add(event);
334 341
       }
335 342
 
336
-      public List<HandlingEvent> findEventsForCargo(TrackingId trackingId) {
337
-        return eventMap.get(trackingId);
343
+      @Override
344
+      public HandlingHistory lookupHandlingHistoryOfCargo(TrackingId trackingId) {
345
+        return new HandlingHistory(eventMap.get(trackingId));
338 346
       }
339 347
     };
340 348