Преглед изворни кода

Introduced asynchronous processing of incoming handling event registration attempts.

Removed the plain-delegation track() method from TrackingService (using repository directly in CargoTrackingController instead).

Fixed a few carrier movement -> voyage references in setters and JSP expressions.

Added cargoIsMisdirected() and cargoHasArrived() to DomainEventNotifier (but no consumers yet).

Routing a cargo is still broken due to database changes incompatible with the random route generation algorithm.
peter_backlund пре 18 година
родитељ
комит
622072310d
27 измењених фајлова са 424 додато и 334 уклоњено
  1. 39
    0
      dddsample/src/main/java/se/citerus/dddsample/application/messaging/CargoHandledConsumer.java
  2. 56
    0
      dddsample/src/main/java/se/citerus/dddsample/application/messaging/HandlingEventRegistrationAttempt.java
  3. 76
    0
      dddsample/src/main/java/se/citerus/dddsample/application/messaging/HandlingEventRegistrationAttemptConsumer.java
  4. 42
    11
      dddsample/src/main/java/se/citerus/dddsample/application/messaging/JmsDomainEventNotifierImpl.java
  5. 1
    1
      dddsample/src/main/java/se/citerus/dddsample/application/remoting/BookingServiceFacadeImpl.java
  6. 1
    1
      dddsample/src/main/java/se/citerus/dddsample/application/routing/ExternalRoutingService.java
  7. 1
    1
      dddsample/src/main/java/se/citerus/dddsample/application/ws/HandlingEventServiceEndpoint.java
  8. 87
    97
      dddsample/src/main/java/se/citerus/dddsample/application/ws/HandlingEventServiceEndpointImpl.java
  9. 22
    0
      dddsample/src/main/java/se/citerus/dddsample/application/ws/RegistrationFailure.java
  10. 3
    1
      dddsample/src/main/java/se/citerus/dddsample/domain/model/ValueObject.java
  11. 4
    2
      dddsample/src/main/java/se/citerus/dddsample/domain/service/DomainEventNotifier.java
  12. 1
    10
      dddsample/src/main/java/se/citerus/dddsample/domain/service/TrackingService.java
  13. 6
    18
      dddsample/src/main/java/se/citerus/dddsample/domain/service/impl/TrackingServiceImpl.java
  14. 7
    7
      dddsample/src/main/java/se/citerus/dddsample/ui/CargoTrackingController.java
  15. 1
    0
      dddsample/src/main/java/se/citerus/routingteam/internal/GraphDAO.java
  16. 23
    8
      dddsample/src/main/resources/context-messaging-jms.xml
  17. 2
    3
      dddsample/src/main/resources/context-remote.xml
  18. 1
    0
      dddsample/src/main/resources/context-service.xml
  19. 1
    1
      dddsample/src/main/webapp/WEB-INF/dispatch-servlet.xml
  20. 2
    2
      dddsample/src/main/webapp/WEB-INF/jsp/admin/show.jsp
  21. 2
    2
      dddsample/src/main/webapp/WEB-INF/jsp/cargo/track.jsp
  22. 3
    1
      dddsample/src/test/java/se/citerus/dddsample/CargoHandlingScenarioTest.java
  23. 1
    1
      dddsample/src/test/java/se/citerus/dddsample/application/routing/ExternalRoutingServiceTest.java
  24. 25
    42
      dddsample/src/test/java/se/citerus/dddsample/application/ws/HandlinEventServiceEndpointTest.java
  25. 1
    1
      dddsample/src/test/java/se/citerus/dddsample/domain/service/RoutingServiceTest.java
  26. 0
    85
      dddsample/src/test/java/se/citerus/dddsample/domain/service/TrackingServiceTest.java
  27. 16
    39
      dddsample/src/test/java/se/citerus/dddsample/ui/CargoTrackingControllerTest.java

+ 39
- 0
dddsample/src/main/java/se/citerus/dddsample/application/messaging/CargoHandledConsumer.java Прегледај датотеку

@@ -0,0 +1,39 @@
1
+package se.citerus.dddsample.application.messaging;
2
+
3
+import org.apache.commons.logging.Log;
4
+import org.apache.commons.logging.LogFactory;
5
+import org.springframework.transaction.annotation.Transactional;
6
+import se.citerus.dddsample.domain.model.cargo.TrackingId;
7
+import se.citerus.dddsample.domain.service.TrackingService;
8
+
9
+import javax.jms.Message;
10
+import javax.jms.MessageListener;
11
+import javax.jms.TextMessage;
12
+
13
+/**
14
+ * Consumes JMS messages and delegates notification of misdirected
15
+ * cargo to the cargo service.
16
+ */
17
+public class CargoHandledConsumer implements MessageListener {
18
+
19
+  private TrackingService trackingService;
20
+  private final Log logger = LogFactory.getLog(getClass());
21
+
22
+  @Transactional(readOnly = true)  
23
+  public void onMessage(final Message message) {
24
+    if (logger.isDebugEnabled()) {
25
+      logger.debug("Received message " + message);
26
+    }
27
+    try {
28
+      final TextMessage textMessage = (TextMessage) message;
29
+      final String trackingidString = textMessage.getText();
30
+      trackingService.onCargoHandled(new TrackingId(trackingidString));
31
+    } catch (Exception e) {
32
+      logger.error(e, e);
33
+    }
34
+  }
35
+
36
+  public void setTrackingService(TrackingService trackingService) {
37
+    this.trackingService = trackingService;
38
+  }
39
+}

+ 56
- 0
dddsample/src/main/java/se/citerus/dddsample/application/messaging/HandlingEventRegistrationAttempt.java Прегледај датотеку

@@ -0,0 +1,56 @@
1
+package se.citerus.dddsample.application.messaging;
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.io.Serializable;
9
+import java.util.Date;
10
+
11
+/**
12
+ * This is a simple data holder for passing incoming handling event
13
+ * registration attempts to proper the registration procedure.
14
+ *  
15
+ */
16
+public class HandlingEventRegistrationAttempt implements Serializable {
17
+
18
+  private final Date date;
19
+  private final TrackingId trackingId;
20
+  private final VoyageNumber voyageNumber;
21
+  private final HandlingEvent.Type type;
22
+  private final UnLocode unLocode;
23
+
24
+  public HandlingEventRegistrationAttempt(final Date date,
25
+                                          final TrackingId trackingId,
26
+                                          final VoyageNumber voyageNumber,
27
+                                          final HandlingEvent.Type type,
28
+                                          final UnLocode unLocode) {
29
+    this.date = date;
30
+    this.trackingId = trackingId;
31
+    this.voyageNumber = voyageNumber;
32
+    this.type = type;
33
+    this.unLocode = unLocode;
34
+  }
35
+
36
+  public Date getDate() {
37
+    return date;
38
+  }
39
+
40
+  public TrackingId getTrackingId() {
41
+    return trackingId;
42
+  }
43
+
44
+  public VoyageNumber getVoyageNumber() {
45
+    return voyageNumber;
46
+  }
47
+
48
+  public HandlingEvent.Type getType() {
49
+    return type;
50
+  }
51
+
52
+  public UnLocode getUnLocode() {
53
+    return unLocode;
54
+  }
55
+  
56
+}

+ 76
- 0
dddsample/src/main/java/se/citerus/dddsample/application/messaging/HandlingEventRegistrationAttemptConsumer.java Прегледај датотеку

@@ -0,0 +1,76 @@
1
+package se.citerus.dddsample.application.messaging;
2
+
3
+import org.apache.commons.logging.Log;
4
+import org.apache.commons.logging.LogFactory;
5
+import org.springframework.transaction.annotation.Transactional;
6
+import se.citerus.dddsample.domain.model.cargo.TrackingId;
7
+import se.citerus.dddsample.domain.model.carrier.VoyageNumber;
8
+import se.citerus.dddsample.domain.model.handling.HandlingEvent;
9
+import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
10
+import se.citerus.dddsample.domain.model.location.UnLocode;
11
+import se.citerus.dddsample.domain.service.HandlingEventService;
12
+import se.citerus.dddsample.domain.service.UnknownCargoException;
13
+import se.citerus.dddsample.domain.service.UnknownLocationException;
14
+import se.citerus.dddsample.domain.service.UnknownVoyageException;
15
+
16
+import javax.jms.JMSException;
17
+import javax.jms.Message;
18
+import javax.jms.MessageListener;
19
+import javax.jms.ObjectMessage;
20
+import java.util.Date;
21
+
22
+/**
23
+ */
24
+public class HandlingEventRegistrationAttemptConsumer implements MessageListener {
25
+
26
+  private HandlingEventFactory handlingEventFactory;
27
+  private HandlingEventService handlingEventService;
28
+  private static final Log logger = LogFactory.getLog(HandlingEventRegistrationAttemptConsumer.class);
29
+
30
+  @Transactional(readOnly = false)
31
+  public void onMessage(Message message) {
32
+    try {
33
+      ObjectMessage om = (ObjectMessage) message;
34
+      HandlingEventRegistrationAttempt attempt = (HandlingEventRegistrationAttempt) om.getObject();
35
+      doRegister(attempt.getDate(), attempt.getTrackingId(), attempt.getVoyageNumber(), attempt.getType(), attempt.getUnLocode());
36
+    } catch (JMSException e) {
37
+      logger.error(e, e);
38
+    }
39
+  }
40
+
41
+  private void doRegister(final Date date, final TrackingId trackingId, final VoyageNumber voyageNumber, final HandlingEvent.Type type, final UnLocode unLocode) {
42
+    try {
43
+        HandlingEvent event = handlingEventFactory.createHandlingEvent(date, trackingId, voyageNumber, unLocode, type);
44
+        handlingEventService.register(event);
45
+    } catch (UnknownVoyageException e) {
46
+      handleUnknownCarrierMovementId(e);
47
+    } catch (UnknownCargoException e) {
48
+      handleUnknownTrackingId(e);
49
+    } catch (UnknownLocationException e) {
50
+      handleUnknownLocation(e);
51
+    }
52
+  }
53
+
54
+  private void handleUnknownLocation(UnknownLocationException e) {
55
+    logger.error(e, e);
56
+  }
57
+
58
+  private void handleUnknownCarrierMovementId(UnknownVoyageException e) {
59
+    logger.error(e, e);
60
+  }
61
+
62
+  private void handleUnknownTrackingId(Exception e) {
63
+    logger.error(e, e);
64
+  }
65
+
66
+  // Setters
67
+
68
+  public void setHandlingEventService(HandlingEventService handlingEventService) {
69
+    this.handlingEventService = handlingEventService;
70
+  }
71
+
72
+  public void setHandlingEventFactory(HandlingEventFactory handlingEventFactory) {
73
+    this.handlingEventFactory = handlingEventFactory;
74
+  }
75
+
76
+}

+ 42
- 11
dddsample/src/main/java/se/citerus/dddsample/application/messaging/JmsDomainEventNotifierImpl.java Прегледај датотеку

@@ -2,27 +2,49 @@ package se.citerus.dddsample.application.messaging;
2 2
 
3 3
 import org.springframework.jms.core.JmsOperations;
4 4
 import org.springframework.jms.core.MessageCreator;
5
-import se.citerus.dddsample.domain.model.cargo.TrackingId;
5
+import se.citerus.dddsample.domain.model.cargo.Cargo;
6 6
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
7 7
 import se.citerus.dddsample.domain.service.DomainEventNotifier;
8 8
 
9
-import javax.jms.*;
9
+import javax.jms.Destination;
10
+import javax.jms.JMSException;
11
+import javax.jms.Message;
12
+import javax.jms.Session;
10 13
 
11 14
 /**
12 15
  * JMS based implementation.
13 16
  */
14 17
 public final class JmsDomainEventNotifierImpl implements DomainEventNotifier {
18
+
15 19
   private JmsOperations jmsOperations;
16
-  private Destination destination;
17
-  public static final String TRACKING_ID_KEY = TrackingId.class.getName() + ".KEY";
20
+  private Destination cargoHandledTopic;
21
+  private Destination misdirectedCargoTopic;
22
+  private Destination deliveredCargoTopic;
18 23
 
24
+  @Override
19 25
   public void cargoWasHandled(final HandlingEvent event) {
20
-    // TODO richer message type
21
-    jmsOperations.send(destination, new MessageCreator() {
26
+    final Cargo cargo = event.cargo();
27
+    jmsOperations.send(cargoHandledTopic, new MessageCreator() {
22 28
       public Message createMessage(final Session session) throws JMSException {
23
-        final MapMessage message = session.createMapMessage();
24
-        message.setStringProperty(TRACKING_ID_KEY, event.cargo().trackingId().idString());
25
-        return message;
29
+        return session.createTextMessage(cargo.trackingId().idString());
30
+      }
31
+    });
32
+  }
33
+
34
+  @Override
35
+  public void cargoWasMisdirected(final Cargo cargo) {
36
+    jmsOperations.send(misdirectedCargoTopic, new MessageCreator() {
37
+      public Message createMessage(Session session) throws JMSException {
38
+        return session.createTextMessage(cargo.trackingId().idString());
39
+      }
40
+    });
41
+  }
42
+
43
+  @Override
44
+  public void cargoHasArrived(final Cargo cargo) {
45
+    jmsOperations.send(deliveredCargoTopic, new MessageCreator() {
46
+      public Message createMessage(Session session) throws JMSException {
47
+        return session.createTextMessage(cargo.trackingId().idString());
26 48
       }
27 49
     });
28 50
   }
@@ -31,7 +53,16 @@ public final class JmsDomainEventNotifierImpl implements DomainEventNotifier {
31 53
     this.jmsOperations = jmsOperations;
32 54
   }
33 55
 
34
-  public void setDestination(final Destination destination) {
35
-    this.destination = destination;
56
+  public void setCargoHandledTopic(final Destination cargoHandledTopic) {
57
+    this.cargoHandledTopic = cargoHandledTopic;
58
+  }
59
+
60
+  public void setMisdirectedCargoTopic(Destination misdirectedCargoTopic) {
61
+    this.misdirectedCargoTopic = misdirectedCargoTopic;
62
+  }
63
+
64
+  public void setDeliveredCargoTopic(Destination deliveredCargoTopic) {
65
+    this.deliveredCargoTopic = deliveredCargoTopic;
36 66
   }
67
+  
37 68
 }

+ 1
- 1
dddsample/src/main/java/se/citerus/dddsample/application/remoting/BookingServiceFacadeImpl.java Прегледај датотеку

@@ -109,7 +109,7 @@ public class BookingServiceFacadeImpl implements BookingServiceFacade {
109 109
     this.cargoRepository = cargoRepository;
110 110
   }
111 111
 
112
-  public void setCarrierMovementRepository(VoyageRepository voyageRepository) {
112
+  public void setVoyageRepository(VoyageRepository voyageRepository) {
113 113
     this.voyageRepository = voyageRepository;
114 114
   }
115 115
 }

+ 1
- 1
dddsample/src/main/java/se/citerus/dddsample/application/routing/ExternalRoutingService.java Прегледај датотеку

@@ -78,7 +78,7 @@ public class ExternalRoutingService implements RoutingService {
78 78
     this.locationRepository = locationRepository;
79 79
   }
80 80
 
81
-  public void setCarrierMovementRepository(VoyageRepository voyageRepository) {
81
+  public void setVoyageRepository(VoyageRepository voyageRepository) {
82 82
     this.voyageRepository = voyageRepository;
83 83
   }
84 84
 }

+ 1
- 1
dddsample/src/main/java/se/citerus/dddsample/application/ws/HandlingEventServiceEndpoint.java Прегледај датотеку

@@ -17,7 +17,7 @@ public interface HandlingEventServiceEndpoint {
17 17
    * @param unlocode          United Nations Location Code for the location where the event occured
18 18
    * @param eventType         type of event
19 19
    */
20
-  void register(String completionTime, String trackingId, String carrierMovementId, String unlocode, String eventType);
20
+  void register(String completionTime, String trackingId, String carrierMovementId, String unlocode, String eventType) throws RegistrationFailure;
21 21
 
22 22
   // TODO structured class that holds these fields, and/or a batching method that accepts a list of those
23 23
   // TODO contract-first instead of code-first (?)

+ 87
- 97
dddsample/src/main/java/se/citerus/dddsample/application/ws/HandlingEventServiceEndpointImpl.java Прегледај датотеку

@@ -3,137 +3,127 @@ package se.citerus.dddsample.application.ws;
3 3
 import org.apache.commons.lang.StringUtils;
4 4
 import org.apache.commons.logging.Log;
5 5
 import org.apache.commons.logging.LogFactory;
6
-import org.springframework.transaction.PlatformTransactionManager;
7
-import org.springframework.transaction.TransactionStatus;
8
-import org.springframework.transaction.support.TransactionCallbackWithoutResult;
9
-import org.springframework.transaction.support.TransactionTemplate;
6
+import org.springframework.jms.core.JmsOperations;
7
+import org.springframework.jms.core.MessageCreator;
8
+import se.citerus.dddsample.application.messaging.HandlingEventRegistrationAttempt;
10 9
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
11 10
 import se.citerus.dddsample.domain.model.carrier.VoyageNumber;
12 11
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
13
-import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
14 12
 import se.citerus.dddsample.domain.model.location.UnLocode;
15
-import se.citerus.dddsample.domain.service.HandlingEventService;
16
-import se.citerus.dddsample.domain.service.UnknownCargoException;
17
-import se.citerus.dddsample.domain.service.UnknownLocationException;
18
-import se.citerus.dddsample.domain.service.UnknownVoyageException;
19 13
 
14
+import javax.jms.JMSException;
15
+import javax.jms.Message;
16
+import javax.jms.Queue;
17
+import javax.jms.Session;
20 18
 import javax.jws.WebService;
21 19
 import java.text.ParseException;
22 20
 import java.text.SimpleDateFormat;
21
+import java.util.ArrayList;
22
+import java.util.Arrays;
23 23
 import java.util.Date;
24
-
24
+import java.util.List;
25
+
26
+/**
27
+ * This web service endpoint implementation performs basic validation and parsing
28
+ * of incoming data, and in case of a valid registration attempt, sends an asynchronous message
29
+ * with the informtion to the handling event registration system for proper registration.
30
+ *  
31
+ */
25 32
 @WebService(endpointInterface = "se.citerus.dddsample.application.ws.HandlingEventServiceEndpoint")
26 33
 public class HandlingEventServiceEndpointImpl implements HandlingEventServiceEndpoint {
27 34
 
28
-  private HandlingEventFactory handlingEventFactory;
29
-  private HandlingEventService handlingEventService;
30
-  private TransactionTemplate transactionTemplate;
31
-  private final Log logger = LogFactory.getLog(getClass());
32
-  protected static final String ISO_8601_FORMAT = "yyyy-mm-dd HH:MM:SS.SSS";
35
+  private JmsOperations jmsOperations;
36
+  private Queue handlingEventQueue;
37
+  private static final Log logger = LogFactory.getLog(HandlingEventServiceEndpointImpl.class);
38
+  
39
+  public static final String ISO_8601_FORMAT = "yyyy-mm-dd HH:MM:SS.SSS";
33 40
 
34 41
   public void register(final String completionTime, final String trackingId, final String voyageNumberString,
35
-                       final String unlocode, final String eventType) {
36
-    try {
37
-      final Date date = parseIso8601Date(completionTime);
38
-      final TrackingId tid = new TrackingId(trackingId);
39
-
40
-      final VoyageNumber voyageNumber;
41
-      if (StringUtils.isNotEmpty(voyageNumberString)) {
42
-        voyageNumber = new VoyageNumber(voyageNumberString);
43
-      } else {
44
-        voyageNumber = null;
45
-      }
46
-
47
-      final HandlingEvent.Type type = parseEventType(eventType);
48
-      final UnLocode ul = new UnLocode(unlocode);
49
-
50
-      doRegister(date, tid, voyageNumber, type, ul);
51
-    } catch (IllegalArgumentException iae) {
52
-      handleIllegalArgument(iae);
53
-    } catch (ParseException pe) {
54
-      handleInvalidDateFormat(completionTime);
55
-    } catch (InvalidEventTypeException iete) {
56
-      handleInvalidEventType(iete);
57
-    } catch (Exception e) {
58
-      handleOtherError(e);
42
+                       final String unlocode, final String eventType) throws RegistrationFailure {
43
+    final List<String> errors = new ArrayList<String>();
44
+
45
+    final Date date = parseDate(completionTime, errors);
46
+    final TrackingId tid = parseTrackingId(trackingId, errors);
47
+    final VoyageNumber voyageNumber = parseVoyageNumber(voyageNumberString, errors);
48
+    final HandlingEvent.Type type = parseEventType(eventType, errors);
49
+    final UnLocode ul = parseUnLocode(unlocode, errors);
50
+
51
+    if (errors.isEmpty()) {
52
+      sendRegistrationAttemptMessage(date, tid, voyageNumber, type, ul);
53
+    } else {
54
+      logger.info("Handling event registration attempt failed: " + errors);
55
+      throw new RegistrationFailure(errors);
59 56
     }
60 57
   }
61 58
 
62
-  // TODO this entire step would be well suited to move to a consumer of asynchronous messages
63
-  private void doRegister(final Date date, final TrackingId tid, final VoyageNumber voyageNumber, final HandlingEvent.Type type, final UnLocode ul) {
64
-    // Using programmatic demarcation here due to weaving conflicts
65
-    // between jax-ws and Spring transaction annotations
66
-    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
67
-        protected void doInTransactionWithoutResult(TransactionStatus status) {
68
-            try {
69
-                HandlingEvent event = handlingEventFactory.createHandlingEvent(date, tid, voyageNumber, ul, type);
70
-                handlingEventService.register(event);
71
-            } catch (UnknownVoyageException e) {
72
-                handleUnknownCarrierMovementId(e);
73
-            } catch (UnknownCargoException e) {
74
-                handleUnknownTrackingId(e);
75
-            } catch (UnknownLocationException e) {
76
-                handleUnknownLocation(e);
77
-            }
78
-        }
59
+  private void sendRegistrationAttemptMessage(final Date date, final TrackingId tid, final VoyageNumber voyageNumber, final HandlingEvent.Type type, final UnLocode ul) {
60
+    jmsOperations.send(handlingEventQueue, new MessageCreator() {
61
+      public Message createMessage(Session session) throws JMSException {
62
+        final HandlingEventRegistrationAttempt attempt = new HandlingEventRegistrationAttempt(date, tid, voyageNumber, type, ul);
63
+        return session.createObjectMessage(attempt);
64
+      }
79 65
     });
66
+    if (logger.isDebugEnabled()) {
67
+      logger.debug("Incoming handling event registration attempt added to queue");
68
+    }
80 69
   }
81 70
 
82
-  private HandlingEvent.Type parseEventType(final String eventType) throws InvalidEventTypeException {
71
+  private UnLocode parseUnLocode(final String unlocode, final List<String> errors) {
83 72
     try {
84
-      return HandlingEvent.Type.valueOf(eventType);
73
+      return new UnLocode(unlocode);
85 74
     } catch (IllegalArgumentException e) {
86
-      throw new InvalidEventTypeException(eventType);
75
+      errors.add(e.getMessage());
76
+      return null;
87 77
     }
88 78
   }
89 79
 
90
-  private Date parseIso8601Date(final String completionTime) throws ParseException {
91
-    return new SimpleDateFormat(ISO_8601_FORMAT).parse(completionTime);
92
-  }
93
-
94
-  // Validation/translation errors
95
-
96
-  private void handleIllegalArgument(IllegalArgumentException iae) {
97
-    logger.error(iae, iae);
98
-  }
99
-
100
-  private void handleOtherError(Exception e) {
101
-    logger.error(e, e);
102
-  }
103
-
104
-  private void handleInvalidEventType(InvalidEventTypeException iete) {
105
-    logger.error(iete, iete);
106
-  }
107
-
108
-  private void handleInvalidDateFormat(String completionTime) {
109
-    logger.error("Invalid date format: " + completionTime + ", must be on ISO 8601 format: " + ISO_8601_FORMAT);
110
-  }
111
-
112
-  // Domain errors, don't belong here really
113
-
114
-  private void handleUnknownLocation(UnknownLocationException e) {
115
-    logger.error(e, e);
80
+  private TrackingId parseTrackingId(final String trackingId, final List<String> errors) {
81
+    try {
82
+      return new TrackingId(trackingId);
83
+    } catch (IllegalArgumentException e) {
84
+      errors.add(e.getMessage());
85
+      return null;
86
+    }
116 87
   }
117 88
 
118
-  private void handleUnknownCarrierMovementId(UnknownVoyageException e) {
119
-    logger.error(e, e);
89
+  private VoyageNumber parseVoyageNumber(final String voyageNumber, final List<String> errors) {
90
+    if (StringUtils.isNotEmpty(voyageNumber)) {
91
+      try {
92
+        return new VoyageNumber(voyageNumber);
93
+      } catch (IllegalArgumentException e) {
94
+        errors.add(e.getMessage());
95
+        return null;
96
+      }
97
+    } else {
98
+      return null;
99
+    }
120 100
   }
121 101
 
122
-  private void handleUnknownTrackingId(Exception e) {
123
-    logger.error(e, e);
102
+  private Date parseDate(final String completionTime, final List<String> errors) {
103
+    Date date;
104
+    try {
105
+      date = new SimpleDateFormat(ISO_8601_FORMAT).parse(completionTime);
106
+    } catch (ParseException e) {
107
+      errors.add("Invalid date format: " + completionTime + ", must be on ISO 8601 format: " + ISO_8601_FORMAT);
108
+      date = null;
109
+    }
110
+    return date;
124 111
   }
125 112
 
126
-  // Setters
127
-
128
-  public void setHandlingEventService(HandlingEventService handlingEventService) {
129
-    this.handlingEventService = handlingEventService;
113
+  private HandlingEvent.Type parseEventType(final String eventType, final List<String> errors) {
114
+    try {
115
+      return HandlingEvent.Type.valueOf(eventType);
116
+    } catch (IllegalArgumentException e) {
117
+      errors.add(eventType + " is not a valid handling event type. Valid types are: " + Arrays.toString(HandlingEvent.Type.values()));
118
+      return null;      
119
+    }
130 120
   }
131 121
 
132
-  public void setTransactionManager(PlatformTransactionManager transactionManager) {
133
-      transactionTemplate = new TransactionTemplate(transactionManager);
122
+  public void setJmsOperations(final JmsOperations jmsOperations) {
123
+    this.jmsOperations = jmsOperations;
134 124
   }
135 125
 
136
-  public void setHandlingEventFactory(HandlingEventFactory handlingEventFactory) {
137
-    this.handlingEventFactory = handlingEventFactory;
126
+  public void setHandlingEventQueue(final Queue handlingEventQueue) {
127
+    this.handlingEventQueue = handlingEventQueue;
138 128
   }
139 129
 }

+ 22
- 0
dddsample/src/main/java/se/citerus/dddsample/application/ws/RegistrationFailure.java Прегледај датотеку

@@ -0,0 +1,22 @@
1
+package se.citerus.dddsample.application.ws;
2
+
3
+import java.util.Arrays;
4
+import java.util.List;
5
+
6
+public class RegistrationFailure extends Exception {
7
+  private final String[] errors;
8
+
9
+  RegistrationFailure(final List<String> errors) {
10
+    this.errors = errors.toArray(new String[errors.size()]);
11
+  }
12
+
13
+  public String[] getErrors() {
14
+    return errors;
15
+  }
16
+
17
+  @Override
18
+  public String getMessage() {
19
+    return "Reistration failure: " + Arrays.toString(errors);
20
+  }
21
+  
22
+}

+ 3
- 1
dddsample/src/main/java/se/citerus/dddsample/domain/model/ValueObject.java Прегледај датотеку

@@ -1,10 +1,12 @@
1 1
 package se.citerus.dddsample.domain.model;
2 2
 
3
+import java.io.Serializable;
4
+
3 5
 /**
4 6
  * A value object, as described in the DDD book.
5 7
  * 
6 8
  */
7
-public interface ValueObject<T> {
9
+public interface ValueObject<T> extends Serializable {
8 10
 
9 11
   /**
10 12
    * Value objects compare by the values of their attributes, they don't have an identity.

+ 4
- 2
dddsample/src/main/java/se/citerus/dddsample/domain/service/DomainEventNotifier.java Прегледај датотеку

@@ -1,5 +1,6 @@
1 1
 package se.citerus.dddsample.domain.service;
2 2
 
3
+import se.citerus.dddsample.domain.model.cargo.Cargo;
3 4
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
4 5
 
5 6
 /**
@@ -20,9 +21,10 @@ public interface DomainEventNotifier {
20 21
    */
21 22
   void cargoWasHandled(HandlingEvent event);
22 23
 
23
-  //void cargoWasMisdirected(Cargo cargo);
24
+  void cargoWasMisdirected(Cargo cargo);
24 25
 
25
-  //void cargoHasArrived(Cargo cargo);
26
+  void cargoHasArrived(Cargo cargo);
26 27
 
27 28
   //void scheduleWasChanged(Voyage voyage);
29
+
28 30
 }

+ 1
- 10
dddsample/src/main/java/se/citerus/dddsample/domain/service/TrackingService.java Прегледај датотеку

@@ -1,6 +1,5 @@
1 1
 package se.citerus.dddsample.domain.service;
2 2
 
3
-import se.citerus.dddsample.domain.model.cargo.Cargo;
4 3
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
5 4
 
6 5
 /**
@@ -10,20 +9,12 @@ import se.citerus.dddsample.domain.model.cargo.TrackingId;
10 9
 public interface TrackingService {
11 10
 
12 11
   /**
13
-   * Track a particular cargo.
14
-   *
15
-   * @param trackingId cargo tracking id
16
-   * @return A cargo and its delivery history, or null if no cargo with given tracking id is found.
17
-   */
18
-  Cargo track(TrackingId trackingId);
19
-
20
-  /**
21 12
    * Inspect cargo and send relevant notifications to interested parties,
22 13
    * for example if a cargo has been misdirected, or unloaded
23 14
    * at the final destination.
24 15
    *
25 16
    * @param trackingId cargo tracking id
26 17
    */
27
-  void inspectCargo(TrackingId trackingId);
18
+  void onCargoHandled(TrackingId trackingId);
28 19
 
29 20
 }

+ 6
- 18
dddsample/src/main/java/se/citerus/dddsample/domain/service/impl/TrackingServiceImpl.java Прегледај датотеку

@@ -20,20 +20,9 @@ public class TrackingServiceImpl implements TrackingService {
20 20
     this.cargoRepository = cargoRepository;
21 21
   }
22 22
 
23
-  public Cargo track(final TrackingId trackingId) {
24
-    // TODO this does not add any value over calling repository
25
-    // Perhaps this service should be remodeled to only handle inspection
26
-    // and state updating 
27
-    Validate.notNull(trackingId);
28
-
29
-    return cargoRepository.find(trackingId);
30
-  }
31
-
32
-  public void inspectCargo(final TrackingId trackingId) {
33
-    // TODO this method name is not descriptive enough
34
-    // For example, onCargoHandling(), whenCargoIsHandled(), actOnHandling() 
35
-    // mirrors DomainEventNotifier.cargoWasHandled()
36
-    Validate.notNull(trackingId);
23
+  @Override
24
+  public void onCargoHandled(final TrackingId trackingId) {
25
+    Validate.notNull(trackingId, "Tracking ID is required");
37 26
 
38 27
     final Cargo cargo = cargoRepository.find(trackingId);
39 28
     if (cargo == null) {
@@ -41,16 +30,15 @@ public class TrackingServiceImpl implements TrackingService {
41 30
       return;
42 31
     }
43 32
 
44
-    // TODO publish events here
33
+    // TODO cargo delivery status update would happen here
45 34
 
46 35
     if (cargo.isMisdirected()) {
47
-      //domainEventNotifier.cargoWasMisdirected(cargo);
36
+      domainEventNotifier.cargoWasMisdirected(cargo);
48 37
     }
49 38
 
50 39
     if (cargo.isUnloadedAtDestination()) {
51
-      //domainEventNotifier.cargoHasArrived(cargo);
40
+      domainEventNotifier.cargoHasArrived(cargo);
52 41
     }
53
-    
54 42
   }
55 43
 
56 44
 }

+ 7
- 7
dddsample/src/main/java/se/citerus/dddsample/ui/CargoTrackingController.java Прегледај датотеку

@@ -6,8 +6,8 @@ import org.springframework.web.servlet.ModelAndView;
6 6
 import org.springframework.web.servlet.mvc.SimpleFormController;
7 7
 import org.springframework.web.servlet.support.RequestContextUtils;
8 8
 import se.citerus.dddsample.domain.model.cargo.Cargo;
9
+import se.citerus.dddsample.domain.model.cargo.CargoRepository;
9 10
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
10
-import se.citerus.dddsample.domain.service.TrackingService;
11 11
 import se.citerus.dddsample.ui.command.TrackCommand;
12 12
 
13 13
 import javax.servlet.http.HttpServletRequest;
@@ -33,7 +33,7 @@ import java.util.Map;
33 33
  */
34 34
 public final class CargoTrackingController extends SimpleFormController {
35 35
 
36
-  private TrackingService trackingService;
36
+  private CargoRepository cargoRepository;
37 37
 
38 38
   public CargoTrackingController() {
39 39
     setCommandClass(TrackCommand.class);
@@ -45,7 +45,8 @@ public final class CargoTrackingController extends SimpleFormController {
45 45
 
46 46
     final TrackCommand trackCommand = (TrackCommand) command;
47 47
     final String trackingIdString = trackCommand.getTrackingId();
48
-    final Cargo cargo = trackingService.track(new TrackingId(trackingIdString));
48
+    
49
+    final Cargo cargo = cargoRepository.find(new TrackingId(trackingIdString));
49 50
 
50 51
     final Map<String, CargoTrackingViewAdapter> model = new HashMap();
51 52
     if (cargo != null) {
@@ -53,13 +54,12 @@ public final class CargoTrackingController extends SimpleFormController {
53 54
       final Locale locale = RequestContextUtils.getLocale(request);
54 55
       model.put("cargo", new CargoTrackingViewAdapter(cargo, messageSource, locale));
55 56
     } else {
56
-      errors.rejectValue("trackingId", "cargo.unknown_id", new Object[]{trackCommand.getTrackingId()},
57
-        "Unknown tracking id");
57
+      errors.rejectValue("trackingId", "cargo.unknown_id", new Object[]{trackCommand.getTrackingId()}, "Unknown tracking id");
58 58
     }
59 59
     return showForm(request, response, errors, model);
60 60
   }
61 61
 
62
-  public void setTrackingService(TrackingService trackingService) {
63
-    this.trackingService = trackingService;
62
+  public void setCargoRepository(CargoRepository cargoRepository) {
63
+    this.cargoRepository = cargoRepository;
64 64
   }
65 65
 }

+ 1
- 0
dddsample/src/main/java/se/citerus/routingteam/internal/GraphDAO.java Прегледај датотеку

@@ -29,6 +29,7 @@ public class GraphDAO {
29 29
     return result;
30 30
   }
31 31
 
32
+  // TODO adapt to Voyage
32 33
   public void storeCarrierMovementId(String cmId, String from, String to) {
33 34
     final String locationSql = "select id from location where unlocode = ?";
34 35
 

+ 23
- 8
dddsample/src/main/resources/context-messaging-jms.xml Прегледај датотеку

@@ -10,25 +10,40 @@
10 10
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
11 11
 
12 12
 
13
-  <amq:connectionFactory id="jmsConnectionFactory" brokerURL="vm://localhost"/>
13
+  <amq:connectionFactory id="jmsConnectionFactory" brokerURL="vm://localhost?broker.persistent=false"/>
14 14
 
15
-  <amq:topic id="handlingEventTopic" name="HandlingEventTopic" physicalName="HandlingEventTopic"/>
15
+  <amq:topic id="cargoHandledTopic" name="CargoHandledTopic"/>
16
+  <amq:topic id="misdirectedCargoTopic" name="MisdirectedCargoTopic"/>
17
+  <amq:topic id="deliveredCargoTopic" name="DeliveredCargoTopic"/>
18
+  <amq:queue id="handlingEventRegistrationAttemptQueue" name="HandlingEventRegistrationAttemptQueue"/>
16 19
 
17 20
   <jms:listener-container connection-factory="jmsConnectionFactory">
18
-    <jms:listener destination="handlingEventTopic" ref="handlingEventMessageDelegate" />
21
+    <jms:listener destination="cargoHandledTopic" ref="cargoHandledConsumer" />
22
+    <!-- TODO subscribers
23
+    <jms:listener destination="misdirectedCargoTopic" ref="TODO"/>
24
+    <jms:listener destination="deliveredCargoTopic" ref="TODO"/>
25
+    -->
26
+    <jms:listener destination="handlingEventRegistrationAttemptQueue" ref="handlingEventRegistrationAttemptConsumer" />
19 27
   </jms:listener-container>
20 28
 
21
-  <bean id="handlingEventMessageDelegate" class="se.citerus.dddsample.application.messaging.HandlingEventMessageDelegate">
22
-    <property name="trackingService" ref="trackingService"/>
23
-  </bean>
24
-  
25 29
   <bean id="jmsOperations" class="org.springframework.jms.core.JmsTemplate">
26 30
     <property name="connectionFactory" ref="jmsConnectionFactory"/>
27 31
   </bean>
28 32
 
29 33
   <bean id="domainEventNotifier" class="se.citerus.dddsample.application.messaging.JmsDomainEventNotifierImpl">
30 34
     <property name="jmsOperations" ref="jmsOperations"/>
31
-    <property name="destination" ref="handlingEventTopic"/>
35
+    <property name="cargoHandledTopic" ref="cargoHandledTopic"/>
36
+    <property name="misdirectedCargoTopic" ref="misdirectedCargoTopic"/>
37
+    <property name="deliveredCargoTopic" ref="deliveredCargoTopic"/>
38
+  </bean>
39
+
40
+  <bean id="cargoHandledConsumer" class="se.citerus.dddsample.application.messaging.CargoHandledConsumer">
41
+    <property name="trackingService" ref="trackingService"/>
42
+  </bean>
43
+
44
+  <bean id="handlingEventRegistrationAttemptConsumer" class="se.citerus.dddsample.application.messaging.HandlingEventRegistrationAttemptConsumer">
45
+    <property name="handlingEventFactory" ref="handlingEventFactory"/>
46
+    <property name="handlingEventService" ref="handlingEventService"/>
32 47
   </bean>
33 48
   
34 49
 </beans>

+ 2
- 3
dddsample/src/main/resources/context-remote.xml Прегледај датотеку

@@ -22,9 +22,8 @@
22 22
   </wss:bindings>
23 23
 
24 24
   <bean id="handlingEventServiceEndpoint" class="se.citerus.dddsample.application.ws.HandlingEventServiceEndpointImpl">
25
-    <property name="handlingEventService" ref="handlingEventService"/>
26
-    <property name="transactionManager" ref="transactionManager"/>
27
-    <property name="handlingEventFactory" ref="handlingEventFactory"/>
25
+    <property name="jmsOperations" ref="jmsOperations"/>
26
+    <property name="handlingEventQueue" ref="handlingEventRegistrationAttemptQueue"/>
28 27
   </bean>
29 28
 
30 29
   <!-- RMI exposed booking service facade -->

+ 1
- 0
dddsample/src/main/resources/context-service.xml Прегледај датотеку

@@ -15,6 +15,7 @@
15 15
 
16 16
   <bean id="trackingService" class="se.citerus.dddsample.domain.service.impl.TrackingServiceImpl">
17 17
     <constructor-arg ref="cargoRepository"/>
18
+    <constructor-arg ref="domainEventNotifier"/>
18 19
   </bean>
19 20
 
20 21
   <bean id="handlingEventService" class="se.citerus.dddsample.domain.service.impl.HandlingEventServiceImpl">

+ 1
- 1
dddsample/src/main/webapp/WEB-INF/dispatch-servlet.xml Прегледај датотеку

@@ -16,7 +16,7 @@
16 16
     <property name="commandName" value="trackCommand"/>
17 17
     <property name="formView" value="cargo/track"/>
18 18
     <property name="successView" value="start"/>
19
-    <property name="trackingService" ref="trackingService"/>
19
+    <property name="cargoRepository" ref="cargoRepository"/>
20 20
     <property name="validator" ref="trackCommandValidator"/>
21 21
   </bean>
22 22
 

+ 2
- 2
dddsample/src/main/webapp/WEB-INF/jsp/admin/show.jsp Прегледај датотеку

@@ -23,7 +23,7 @@
23 23
         <caption>Itinerary</caption>
24 24
         <thead>
25 25
           <tr>
26
-            <td>Carrier</td>
26
+            <td>Voyage number</td>
27 27
             <td>From</td>
28 28
             <td>To</td>
29 29
           </tr>
@@ -31,7 +31,7 @@
31 31
         <tbody>
32 32
           <c:forEach items="${cargo.legs}" var="leg">
33 33
             <tr>
34
-              <td>${leg.carrierMovementId}</td>
34
+              <td>${leg.voyageNumber}</td>
35 35
               <td>${leg.from}</td>
36 36
               <td>${leg.to}</td>
37 37
             </tr>

+ 2
- 2
dddsample/src/main/webapp/WEB-INF/jsp/cargo/track.jsp Прегледај датотеку

@@ -47,7 +47,7 @@
47 47
             <td>Event</td>
48 48
             <td>Location</td>
49 49
             <td>Time</td>
50
-            <td>Carrier Movement</td>
50
+            <td>Voyage number</td>
51 51
             <td></td>
52 52
           </tr>
53 53
         </thead>
@@ -57,7 +57,7 @@
57 57
               <td>${event.type}</td>
58 58
               <td>${event.location}</td>
59 59
               <td>${event.time}</td>
60
-              <td>${event.carrierMovement}</td>
60
+              <td>${event.voyageNumber}</td>
61 61
               <td>
62 62
                 <img src="${rc.contextPath}/images/${event.expected ? "tick" : "cross"}.png" alt=""/>
63 63
               </td>

+ 3
- 1
dddsample/src/test/java/se/citerus/dddsample/CargoHandlingScenarioTest.java Прегледај датотеку

@@ -142,8 +142,10 @@ public class CargoHandlingScenarioTest extends TestCase {
142 142
     // Synchronous stub
143 143
     domainEventNotifier = new DomainEventNotifier() {
144 144
       public void cargoWasHandled(HandlingEvent event) {
145
-        trackingService.inspectCargo(event.cargo().trackingId());
145
+        trackingService.onCargoHandled(event.cargo().trackingId());
146 146
       }
147
+      public void cargoWasMisdirected(Cargo cargo) {}
148
+      public void cargoHasArrived(Cargo cargo) {}
147 149
     };
148 150
 
149 151
     // Stub

+ 1
- 1
dddsample/src/test/java/se/citerus/dddsample/application/routing/ExternalRoutingServiceTest.java Прегледај датотеку

@@ -27,7 +27,7 @@ public class ExternalRoutingServiceTest extends TestCase {
27 27
   @Override
28 28
   public void setUp() {
29 29
     routingService = new ExternalRoutingService();
30
-    routingService.setCarrierMovementRepository(new VoyageRepositoryInMem());
30
+    routingService.setVoyageRepository(new VoyageRepositoryInMem());
31 31
     routingService.setLocationRepository(new LocationRepositoryInMem());
32 32
 
33 33
     graphTraversalService = createMock(GraphTraversalService.class);

+ 25
- 42
dddsample/src/test/java/se/citerus/dddsample/application/ws/HandlinEventServiceEndpointTest.java Прегледај датотеку

@@ -2,69 +2,52 @@ package se.citerus.dddsample.application.ws;
2 2
 
3 3
 import junit.framework.TestCase;
4 4
 import static org.easymock.EasyMock.*;
5
-import se.citerus.dddsample.application.service.InMemTransactionManager;
6
-import se.citerus.dddsample.domain.model.cargo.Cargo;
7
-import se.citerus.dddsample.domain.model.cargo.TrackingId;
8
-import se.citerus.dddsample.domain.model.carrier.SampleVoyages;
9
-import se.citerus.dddsample.domain.model.carrier.VoyageNumber;
10
-import se.citerus.dddsample.domain.model.handling.HandlingEvent;
11
-import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
12
-import static se.citerus.dddsample.domain.model.location.SampleLocations.HONGKONG;
13
-import static se.citerus.dddsample.domain.model.location.SampleLocations.NEWYORK;
14
-import se.citerus.dddsample.domain.model.location.UnLocode;
15
-import se.citerus.dddsample.domain.service.HandlingEventService;
16
-import se.citerus.dddsample.domain.service.UnknownCargoException;
17
-import se.citerus.dddsample.domain.service.UnknownLocationException;
18
-import se.citerus.dddsample.domain.service.UnknownVoyageException;
5
+import org.springframework.jms.core.JmsOperations;
6
+import org.springframework.jms.core.MessageCreator;
19 7
 
8
+import javax.jms.Queue;
20 9
 import java.text.SimpleDateFormat;
21 10
 import java.util.Date;
22 11
 
23 12
 public class HandlinEventServiceEndpointTest extends TestCase {
24 13
 
25 14
   private HandlingEventServiceEndpointImpl endpoint;
26
-  private HandlingEventService handlingEventService;
27
-  private SimpleDateFormat sdf;
28
-  private HandlingEvent event;
15
+  private SimpleDateFormat sdf = new SimpleDateFormat(HandlingEventServiceEndpointImpl.ISO_8601_FORMAT);
29 16
   private Date date = new Date(100);
17
+  private JmsOperations jmsOperations;
18
+  private Queue queue;
30 19
 
31 20
   protected void setUp() throws Exception {
32
-    sdf = new SimpleDateFormat(HandlingEventServiceEndpointImpl.ISO_8601_FORMAT);
33
-
34 21
     endpoint = new HandlingEventServiceEndpointImpl();
35 22
 
36
-    handlingEventService = createMock(HandlingEventService.class);
37
-    endpoint.setHandlingEventService(handlingEventService);
38
-
39
-    endpoint.setTransactionManager(new InMemTransactionManager());
40
-
41
-    Cargo cargo = new Cargo(new TrackingId("FOO"), HONGKONG, NEWYORK);
42
-
43
-    event = new HandlingEvent(
44
-      cargo, date, new Date(), HandlingEvent.Type.LOAD, HONGKONG, SampleVoyages.CM003
45
-    );
46
-
47
-    HandlingEventFactory handlingEventFactory = new HandlingEventFactory(null,null,null) {
48
-      @Override
49
-      public HandlingEvent createHandlingEvent(Date completionTime, TrackingId trackingId, VoyageNumber voyageNumber, UnLocode unlocode, HandlingEvent.Type type)
50
-        throws UnknownCargoException, UnknownVoyageException, UnknownLocationException {
51
-        
52
-        return event;
53
-      }
54
-    };
23
+    jmsOperations = createMock(JmsOperations.class);
24
+    queue = createMock(Queue.class);
55 25
 
56
-    endpoint.setHandlingEventFactory(handlingEventFactory);
26
+    endpoint.setJmsOperations(jmsOperations);
27
+    endpoint.setHandlingEventQueue(queue);
57 28
   }
58 29
 
59 30
   public void testRegisterValidEvent() throws Exception {
60
-    handlingEventService.register(event);
61
-    replay(handlingEventService);
31
+    jmsOperations.send(eq(queue), isA(MessageCreator.class));
32
+    replay(jmsOperations, queue);
62 33
 
63 34
     // Tested call
64 35
     endpoint.register(sdf.format(date), "FOO", "CAR_456", "CNHKG", "LOAD");
65 36
   }
66 37
 
38
+  public void testRegisterInalidEvent() throws Exception {
39
+    replay(jmsOperations, queue);
40
+
41
+    // Tested call
42
+    try {
43
+      endpoint.register("NOT_A_DATE", "", "", "NOT_A_UN_LOCODE", "NOT_A_TYPE");
44
+      fail("Should not accept invalid ");
45
+    } catch (Exception expected) {
46
+      System.err.println(expected);
47
+    }
48
+  }
49
+
67 50
   protected void tearDown() throws Exception {
68
-    verify(handlingEventService);
51
+    verify(jmsOperations, queue);
69 52
   }
70 53
 }

+ 1
- 1
dddsample/src/test/java/se/citerus/dddsample/domain/service/RoutingServiceTest.java Прегледај датотеку

@@ -31,7 +31,7 @@ public class RoutingServiceTest extends TestCase {
31 31
     routingService.setLocationRepository(locationRepository);
32 32
 
33 33
     voyageRepository = createMock(VoyageRepository.class);
34
-    routingService.setCarrierMovementRepository(voyageRepository);
34
+    routingService.setVoyageRepository(voyageRepository);
35 35
 
36 36
     GraphTraversalService graphTraversalService = new GraphTraversalServiceImpl(new GraphDAO(createMock(DataSource.class)) {
37 37
       public List<String> listLocations() {

+ 0
- 85
dddsample/src/test/java/se/citerus/dddsample/domain/service/TrackingServiceTest.java Прегледај датотеку

@@ -1,85 +0,0 @@
1
-package se.citerus.dddsample.domain.service;
2
-
3
-import junit.framework.TestCase;
4
-import static org.easymock.EasyMock.*;
5
-import se.citerus.dddsample.domain.model.cargo.Cargo;
6
-import se.citerus.dddsample.domain.model.cargo.CargoRepository;
7
-import se.citerus.dddsample.domain.model.cargo.CargoTestHelper;
8
-import se.citerus.dddsample.domain.model.cargo.TrackingId;
9
-import se.citerus.dddsample.domain.model.carrier.SampleVoyages;
10
-import se.citerus.dddsample.domain.model.handling.HandlingEvent;
11
-import static se.citerus.dddsample.domain.model.location.SampleLocations.CHICAGO;
12
-import static se.citerus.dddsample.domain.model.location.SampleLocations.STOCKHOLM;
13
-import se.citerus.dddsample.domain.service.impl.TrackingServiceImpl;
14
-
15
-import java.util.Arrays;
16
-import java.util.Date;
17
-import java.util.List;
18
-
19
-
20
-public class TrackingServiceTest extends TestCase {
21
-
22
-  TrackingServiceImpl cargoService;
23
-  CargoRepository cargoRepository;
24
-  DomainEventNotifier domainEventNotifier;
25
-
26
-  protected void setUp() throws Exception {
27
-    cargoRepository = createMock(CargoRepository.class);
28
-    domainEventNotifier = createMock(DomainEventNotifier.class);
29
-    cargoService = new TrackingServiceImpl(domainEventNotifier, cargoRepository);
30
-  }
31
-
32
-  public void testTrackingScenario() {
33
-    final Cargo cargo = new Cargo(new TrackingId("XYZ"), STOCKHOLM, CHICAGO);
34
-
35
-    HandlingEvent claimed = new HandlingEvent(cargo, new Date(10), new Date(20), HandlingEvent.Type.CLAIM, STOCKHOLM);
36
-
37
-    HandlingEvent loaded = new HandlingEvent(cargo, new Date(12), new Date(25), HandlingEvent.Type.LOAD, STOCKHOLM, SampleVoyages.CM001);
38
-    HandlingEvent unloaded = new HandlingEvent(cargo, new Date(100), new Date(110), HandlingEvent.Type.UNLOAD, CHICAGO, SampleVoyages.CM002);
39
-
40
-    // Add out of order to verify ordering in DTO
41
-    List<HandlingEvent> eventList = Arrays.asList(loaded, unloaded, claimed);
42
-
43
-    CargoTestHelper.setDeliveryHistory(cargo, eventList);
44
-
45
-    expect(cargoRepository.find(new TrackingId("XYZ"))).andReturn(cargo);
46
-
47
-    replay(cargoRepository);
48
-
49
-
50
-    // Tested call
51
-    Cargo trackedCargo = cargoService.track(new TrackingId("XYZ"));
52
-
53
-    assertEquals(cargo, trackedCargo);
54
-
55
-    List<HandlingEvent> events = trackedCargo.delivery().history();
56
-    assertEquals(3, events.size());
57
-
58
-    // Claim happened first
59
-    HandlingEvent handlingEvent = events.get(0);
60
-    assertEquals(claimed, handlingEvent);
61
-
62
-    // Then load
63
-    handlingEvent = events.get(1);
64
-    assertEquals(loaded, handlingEvent);
65
-
66
-    // Finally unload
67
-    handlingEvent = events.get(2);
68
-    assertEquals(unloaded, handlingEvent);
69
-  }
70
-
71
-  public void testTrackNullResult() {
72
-    expect(cargoRepository.find(new TrackingId("XYZ"))).andReturn(null);
73
-    replay(cargoRepository);
74
-
75
-    // Tested call
76
-    Cargo cargo = cargoService.track(new TrackingId("XYZ"));
77
-    
78
-    assertNull(cargo);
79
-  }
80
-
81
-  protected void onTearDown() throws Exception {
82
-    verify(cargoRepository);
83
-  }
84
-
85
-}

+ 16
- 39
dddsample/src/test/java/se/citerus/dddsample/ui/CargoTrackingControllerTest.java Прегледај датотеку

@@ -1,6 +1,7 @@
1 1
 package se.citerus.dddsample.ui;
2 2
 
3 3
 import junit.framework.TestCase;
4
+import org.easymock.EasyMock;
4 5
 import org.springframework.context.support.StaticApplicationContext;
5 6
 import org.springframework.mock.web.MockHttpServletRequest;
6 7
 import org.springframework.mock.web.MockHttpServletResponse;
@@ -10,24 +11,18 @@ import org.springframework.validation.BindingResult;
10 11
 import org.springframework.validation.Errors;
11 12
 import org.springframework.validation.FieldError;
12 13
 import org.springframework.web.servlet.ModelAndView;
13
-import se.citerus.dddsample.domain.model.cargo.Cargo;
14
-import se.citerus.dddsample.domain.model.cargo.CargoTestHelper;
15
-import se.citerus.dddsample.domain.model.cargo.TrackingId;
16
-import se.citerus.dddsample.domain.model.handling.HandlingEvent;
17
-import static se.citerus.dddsample.domain.model.location.SampleLocations.HONGKONG;
18
-import static se.citerus.dddsample.domain.model.location.SampleLocations.TOKYO;
19
-import se.citerus.dddsample.domain.service.TrackingService;
14
+import se.citerus.dddsample.application.persistence.CargoRepositoryInMem;
15
+import se.citerus.dddsample.application.persistence.HandlingEventRepositoryInMem;
16
+import se.citerus.dddsample.domain.model.cargo.CargoRepository;
20 17
 import se.citerus.dddsample.ui.command.TrackCommand;
21 18
 
22
-import java.util.Arrays;
23
-import java.util.Date;
24
-
25 19
 public class CargoTrackingControllerTest extends TestCase {
26 20
   CargoTrackingController controller;
27 21
   MockHttpServletRequest request;
28 22
   MockHttpServletResponse response;
29 23
   MockHttpSession session;
30 24
   MockServletContext servletContext;
25
+  private CargoRepositoryInMem cargoRepository;
31 26
 
32 27
   protected void setUp() throws Exception {
33 28
     servletContext = new MockServletContext("test");
@@ -42,27 +37,13 @@ public class CargoTrackingControllerTest extends TestCase {
42 37
     controller.setFormView("test-form");
43 38
     controller.setSuccessView("test-success");
44 39
     controller.setCommandName("test-command-name");
45
-  }
46
-
47
-  private TrackingService getCargoServiceMock() {
48
-    return new EmptyStubTrackingService() {
49
-
50
-      public Cargo track(TrackingId trackingId) {
51
-        final Cargo cargo = new Cargo(trackingId, HONGKONG, TOKYO);
52
-        final HandlingEvent event = new HandlingEvent(cargo, new Date(10L), new Date(20L), HandlingEvent.Type.RECEIVE, HONGKONG);
53
-        CargoTestHelper.setDeliveryHistory(cargo, Arrays.asList(event));
54
-        
55
-        return cargo;
56
-      }
57
-    };
58
-  }
59
-
60
-  private TrackingService getTrackingServiceNullMock() {
61
-    return new EmptyStubTrackingService();
40
+    cargoRepository = new CargoRepositoryInMem();
41
+    cargoRepository.setHandlingEventRepository(new HandlingEventRepositoryInMem());
42
+    cargoRepository.init();
62 43
   }
63 44
 
64 45
   public void testHandleGet() throws Exception {
65
-    controller.setTrackingService(getCargoServiceMock());
46
+    controller.setCargoRepository(new CargoRepositoryInMem());
66 47
     request.setMethod("GET");
67 48
 
68 49
     ModelAndView mav = controller.handleRequest(request, response);
@@ -73,8 +54,8 @@ public class CargoTrackingControllerTest extends TestCase {
73 54
   }
74 55
 
75 56
   public void testHandlePost() throws Exception {
76
-    controller.setTrackingService(getCargoServiceMock());
77
-    request.addParameter("trackingId", "JKL456");
57
+    controller.setCargoRepository(cargoRepository);
58
+    request.addParameter("trackingId", "ABC");
78 59
     request.setMethod("POST");
79 60
 
80 61
     ModelAndView mav = controller.handleRequest(request, response);
@@ -83,11 +64,14 @@ public class CargoTrackingControllerTest extends TestCase {
83 64
     // Errors, command are two standard map attributes, the third should be the cargo object
84 65
     assertEquals(3, mav.getModel().size());
85 66
     CargoTrackingViewAdapter cargo = (CargoTrackingViewAdapter) mav.getModel().get("cargo");
86
-    assertEquals("JKL456", cargo.getTrackingId());
67
+    assertEquals("ABC", cargo.getTrackingId());
87 68
   }
88 69
 
89 70
   public void testUnknownCargo() throws Exception {
90
-    controller.setTrackingService(getTrackingServiceNullMock());
71
+    CargoRepository cargoRepository = EasyMock.createNiceMock(CargoRepository.class);
72
+    EasyMock.replay(cargoRepository);
73
+    controller.setCargoRepository(cargoRepository);
74
+    
91 75
     request.setMethod("POST");
92 76
     request.setParameter("trackingId", "unknown-id");
93 77
 
@@ -106,11 +90,4 @@ public class CargoTrackingControllerTest extends TestCase {
106 90
     assertEquals(command.getTrackingId(), fe.getArguments()[0]);
107 91
   }
108 92
 
109
-  private class EmptyStubTrackingService implements TrackingService {
110
-    public Cargo track(TrackingId trackingId) {
111
-      return null;
112
-    }
113
-    public void inspectCargo(TrackingId trackingId) {
114
-    }
115
-  }
116 93
 }