소스 검색

Implemented a very efficient, randomized, routing service.

peter_backlund 18 년 전
부모
커밋
c0a5de29c5

+ 4
- 0
dddsample/src/main/java/se/citerus/dddsample/repository/LocationRepository.java 파일 보기

@@ -3,8 +3,12 @@ package se.citerus.dddsample.repository;
3 3
 import se.citerus.dddsample.domain.Location;
4 4
 import se.citerus.dddsample.domain.UnLocode;
5 5
 
6
+import java.util.List;
7
+
6 8
 public interface LocationRepository {
7 9
 
8 10
   Location find(UnLocode unLocode);
9 11
 
12
+  List<Location> findAll();
13
+
10 14
 }

+ 7
- 0
dddsample/src/main/java/se/citerus/dddsample/repository/LocationRepositoryHibernate.java 파일 보기

@@ -3,6 +3,8 @@ package se.citerus.dddsample.repository;
3 3
 import se.citerus.dddsample.domain.Location;
4 4
 import se.citerus.dddsample.domain.UnLocode;
5 5
 
6
+import java.util.List;
7
+
6 8
 public class LocationRepositoryHibernate extends HibernateRepository implements LocationRepository {
7 9
 
8 10
   public Location find(UnLocode unLocode) {
@@ -11,4 +13,9 @@ public class LocationRepositoryHibernate extends HibernateRepository implements
11 13
           setParameter(0, unLocode).
12 14
           uniqueResult();
13 15
   }
16
+
17
+  public List<Location> findAll() {
18
+    return getSession().createQuery("from Location").list();
19
+  }
20
+
14 21
 }

+ 52
- 0
dddsample/src/main/java/se/citerus/dddsample/service/RoutingServiceImpl.java 파일 보기

@@ -0,0 +1,52 @@
1
+package se.citerus.dddsample.service;
2
+
3
+import org.springframework.transaction.annotation.Transactional;
4
+import se.citerus.dddsample.domain.*;
5
+import se.citerus.dddsample.repository.LocationRepository;
6
+
7
+import java.util.*;
8
+
9
+/**
10
+ * Simple routing service implementation that randomly creates a number
11
+ * of different itineraries.
12
+ *
13
+ */
14
+public class RoutingServiceImpl implements RoutingService {
15
+
16
+  LocationRepository locationRepository;
17
+
18
+  @Transactional(readOnly = true)
19
+  public Set<Itinerary> calculatePossibleRoutes(Cargo cargo, Specification specification) {
20
+    List<Location> allLocations = locationRepository.findAll();
21
+
22
+    allLocations.remove(cargo.origin());
23
+    allLocations.remove(cargo.finalDestination());
24
+
25
+    // TODO: vary the number of locations and number of candidates randomly
26
+
27
+    int candidateCount = 3;
28
+    Set<Itinerary> candidates = new HashSet<Itinerary>(candidateCount);
29
+    for (int i = 0; i < candidateCount; i++) {
30
+      Collections.shuffle(allLocations);
31
+      List<Leg> legs = new ArrayList<Leg>(allLocations.size() - 1);
32
+
33
+      legs.add(new Leg(new CarrierMovementId("CM000"), cargo.origin(), allLocations.get(0)));
34
+      for (int j = 0; j < allLocations.size() - 1 ; j++) {
35
+        legs.add(new Leg(new CarrierMovementId("CM00" + j), 
36
+          allLocations.get(j), allLocations.get(j + 1)));
37
+      }
38
+      legs.add(new Leg(new CarrierMovementId("CM999"), allLocations.get(allLocations.size() - 1), cargo.finalDestination()));
39
+
40
+      Itinerary itinerary = new Itinerary(legs);
41
+
42
+      candidates.add(itinerary);
43
+    }
44
+
45
+    return candidates;
46
+  }
47
+
48
+  public void setLocationRepository(LocationRepository locationRepository) {
49
+    this.locationRepository = locationRepository;
50
+  }
51
+
52
+}

+ 4
- 0
dddsample/src/main/resources/context-service.xml 파일 보기

@@ -30,6 +30,10 @@
30 30
     <property name="destination" ref="handlingEventTopic"/>
31 31
   </bean>
32 32
 
33
+  <bean id="routingService" class="se.citerus.dddsample.service.RoutingServiceImpl">
34
+    <property name="locationRepository" ref="locationRepository"/>
35
+  </bean>
36
+
33 37
   <!--
34 38
   <bean id="eventService" class="se.citerus.dddsample.service.ThreadBasedEventServiceImpl">
35 39
     <property name="cargoService" ref="cargoService"/>

+ 19
- 19
dddsample/src/test/java/se/citerus/dddsample/domain/DeliveryHistoryTest.java 파일 보기

@@ -1,28 +1,28 @@
1 1
 package se.citerus.dddsample.domain;
2 2
 
3 3
 import junit.framework.TestCase;
4
+import static se.citerus.dddsample.domain.SampleLocations.*;
4 5
 
5 6
 import java.text.DateFormat;
6 7
 import java.text.SimpleDateFormat;
7
-import java.util.*;
8
+import java.util.Arrays;
9
+import java.util.Date;
10
+import java.util.List;
8 11
 
9 12
 public class DeliveryHistoryTest extends TestCase {
10
-  private Location ham = new Location(new UnLocode("DE", "HAM"), "Hamburg");
11
-  private Location from = new Location(new UnLocode("FR", "OMX"), "From");
12
-  private Location to = new Location(new UnLocode("TO", "XXX"), "To");
13
-  private Cargo cargo = new Cargo(new TrackingId("XYZ"), from, to);
14 13
 
14
+  private Cargo cargo = new Cargo(new TrackingId("XYZ"), HONGKONG, NEWYORK);
15 15
 
16 16
   public void testEvensOrderedByTimeOccured() throws Exception {
17 17
     DeliveryHistory dh = new DeliveryHistory();
18 18
     assertTrue(dh.eventsOrderedByCompletionTime().isEmpty());
19 19
 
20 20
     DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
21
-    CarrierMovement carrierMovement = new CarrierMovement(new CarrierMovementId("CAR_001"), from, to);
22
-    HandlingEvent he1 = new HandlingEvent(cargo, df.parse("2010-01-03"), new Date(), HandlingEvent.Type.RECEIVE, to, null);
23
-    HandlingEvent he2 = new HandlingEvent(cargo, df.parse("2010-01-01"), new Date(), HandlingEvent.Type.LOAD, to, carrierMovement);
24
-    HandlingEvent he3 = new HandlingEvent(cargo, df.parse("2010-01-04"), new Date(), HandlingEvent.Type.CLAIM, from, null);
25
-    HandlingEvent he4 = new HandlingEvent(cargo, df.parse("2010-01-02"), new Date(), HandlingEvent.Type.UNLOAD, from, carrierMovement);
21
+    CarrierMovement carrierMovement = new CarrierMovement(new CarrierMovementId("CAR_001"), HONGKONG, NEWYORK);
22
+    HandlingEvent he1 = new HandlingEvent(cargo, df.parse("2010-01-03"), new Date(), HandlingEvent.Type.RECEIVE, NEWYORK, null);
23
+    HandlingEvent he2 = new HandlingEvent(cargo, df.parse("2010-01-01"), new Date(), HandlingEvent.Type.LOAD, NEWYORK, carrierMovement);
24
+    HandlingEvent he3 = new HandlingEvent(cargo, df.parse("2010-01-04"), new Date(), HandlingEvent.Type.CLAIM, HONGKONG, null);
25
+    HandlingEvent he4 = new HandlingEvent(cargo, df.parse("2010-01-02"), new Date(), HandlingEvent.Type.UNLOAD, HONGKONG, carrierMovement);
26 26
     dh.addAllEvents(Arrays.asList(he1, he2, he3, he4));
27 27
 
28 28
     List<HandlingEvent> orderEvents = dh.eventsOrderedByCompletionTime();
@@ -38,17 +38,17 @@ public class DeliveryHistoryTest extends TestCase {
38 38
 
39 39
     assertEquals(StatusCode.NOT_RECIEVED, deliveryHistory.status());
40 40
 
41
-    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(10), new Date(11), HandlingEvent.Type.RECEIVE, ham, null));
41
+    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(10), new Date(11), HandlingEvent.Type.RECEIVE, HAMBURG, null));
42 42
     assertEquals(StatusCode.IN_PORT, deliveryHistory.status());
43 43
 
44
-    CarrierMovement carrierMovement = new CarrierMovement(new CarrierMovementId("ABC"), ham, ham);
45
-    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(20), new Date(21), HandlingEvent.Type.LOAD, ham, carrierMovement));
44
+    CarrierMovement carrierMovement = new CarrierMovement(new CarrierMovementId("ABC"), HAMBURG, HAMBURG);
45
+    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(20), new Date(21), HandlingEvent.Type.LOAD, HAMBURG, carrierMovement));
46 46
     assertEquals(StatusCode.ONBOARD_CARRIER, deliveryHistory.status());
47 47
 
48
-    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(30), new Date(31), HandlingEvent.Type.UNLOAD, ham, carrierMovement));
48
+    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(30), new Date(31), HandlingEvent.Type.UNLOAD, HAMBURG, carrierMovement));
49 49
     assertEquals(StatusCode.IN_PORT, deliveryHistory.status());
50 50
 
51
-    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(40), new Date(41), HandlingEvent.Type.CLAIM, ham, null));
51
+    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(40), new Date(41), HandlingEvent.Type.CLAIM, HAMBURG, null));
52 52
     assertEquals(StatusCode.CLAIMED, deliveryHistory.status());
53 53
   }
54 54
 
@@ -57,11 +57,11 @@ public class DeliveryHistoryTest extends TestCase {
57 57
 
58 58
     assertNull(deliveryHistory.currentLocation());
59 59
 
60
-    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(10), new Date(11), HandlingEvent.Type.RECEIVE, ham, null));
61
-    assertEquals(ham, deliveryHistory.currentLocation());
60
+    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(10), new Date(11), HandlingEvent.Type.RECEIVE, HAMBURG, null));
61
+    assertEquals(HAMBURG, deliveryHistory.currentLocation());
62 62
 
63
-    CarrierMovement carrierMovement = new CarrierMovement(new CarrierMovementId("ABC"), ham, ham);
64
-    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(20), new Date(21), HandlingEvent.Type.LOAD, ham, carrierMovement));
63
+    CarrierMovement carrierMovement = new CarrierMovement(new CarrierMovementId("ABC"), HAMBURG, HAMBURG);
64
+    deliveryHistory.addEvent(new HandlingEvent(cargo, new Date(20), new Date(21), HandlingEvent.Type.LOAD, HAMBURG, carrierMovement));
65 65
     assertNull(deliveryHistory.currentLocation());
66 66
   }
67 67
 

+ 22
- 0
dddsample/src/test/java/se/citerus/dddsample/domain/SampleLocations.java 파일 보기

@@ -1,5 +1,9 @@
1 1
 package se.citerus.dddsample.domain;
2 2
 
3
+import java.lang.reflect.Field;
4
+import java.util.ArrayList;
5
+import java.util.List;
6
+
3 7
 /**
4 8
  * A few locations for easy testing.
5 9
  *
@@ -19,4 +23,22 @@ public class SampleLocations {
19 23
   public static final Location HANGZOU = new Location(new UnLocode("CN", "HGH"), "Hangzhou");
20 24
   public static final Location NEWYORK = new Location(new UnLocode("US", "NYC"), "New York");
21 25
 
26
+  public static final List<Location> all = new ArrayList<Location>();
27
+
28
+  static {
29
+    for (Field field : SampleLocations.class.getDeclaredFields()) {
30
+      if (field.getType().equals(Location.class)) {
31
+        try {
32
+          all.add((Location) field.get(null));
33
+        } catch (IllegalAccessException e) {
34
+          throw new RuntimeException(e);
35
+        }
36
+      }
37
+    }
38
+  }
39
+
40
+  public static List<Location> getAll() {
41
+    return all;
42
+  }
43
+  
22 44
 }

+ 11
- 0
dddsample/src/test/java/se/citerus/dddsample/repository/LocationRepositoryTest.java 파일 보기

@@ -3,6 +3,8 @@ package se.citerus.dddsample.repository;
3 3
 import se.citerus.dddsample.domain.Location;
4 4
 import se.citerus.dddsample.domain.UnLocode;
5 5
 
6
+import java.util.List;
7
+
6 8
 public class LocationRepositoryTest extends AbstractRepositoryTest {
7 9
   private LocationRepository locationRepository;
8 10
   
@@ -11,6 +13,15 @@ public class LocationRepositoryTest extends AbstractRepositoryTest {
11 13
     Location location = locationRepository.find(melbourne);
12 14
     assertNotNull(location);
13 15
     assertEquals(melbourne, location.unLocode());
16
+
17
+    assertNull(locationRepository.find(new UnLocode("NO","LOC")));
18
+  }
19
+
20
+  public void testFindAll() throws Exception {
21
+    List<Location> allLocations = locationRepository.findAll();
22
+
23
+    assertNotNull(allLocations);
24
+    assertEquals(7, allLocations.size());
14 25
   }
15 26
 
16 27
   public void setLocationRepository(LocationRepository locationRepository) {

+ 69
- 0
dddsample/src/test/java/se/citerus/dddsample/service/RoutingScenarioTest.java 파일 보기

@@ -0,0 +1,69 @@
1
+package se.citerus.dddsample.service;
2
+
3
+import junit.framework.TestCase;
4
+import se.citerus.dddsample.domain.*;
5
+import se.citerus.dddsample.repository.CargoRepository;
6
+
7
+import java.util.Date;
8
+import java.util.Set;
9
+
10
+public class RoutingScenarioTest extends TestCase {
11
+
12
+  RoutingService routingService;
13
+  CargoRepository cargoRepository;
14
+  HandlingEventService handlingEventService;
15
+
16
+  public void xtestCalculateRoute() throws Exception {
17
+
18
+    TrackingId trackingId = new TrackingId("XYZ123");
19
+    Cargo cargo = cargoRepository.find(trackingId);
20
+    Specification specification = null;
21
+
22
+    /*
23
+      The routing service calculates a number of possible routes that
24
+      satisfy the given specification (must arrive in three days, must not
25
+      cost more than $10,000 etc).
26
+     */
27
+    Set<Itinerary> itineraryCandidates = routingService.calculatePossibleRoutes(cargo, specification);
28
+
29
+    /*
30
+      Someone, or something, selects the most appropriate itinerary and
31
+      assigns that itinerary to the cargo.
32
+     */
33
+    Itinerary itinerary = stubbedItinerarySelection(itineraryCandidates);
34
+    cargo.setItinerary(itinerary);
35
+
36
+    /*
37
+      A number of events occur, all of which are according to plan
38
+     */
39
+    handlingEventService.register(new Date(), trackingId, new CarrierMovementId("A001"), new UnLocode("SE","STO"), null);
40
+    handlingEventService.register(new Date(), trackingId, new CarrierMovementId("B002"), null, null);
41
+    handlingEventService.register(new Date(), trackingId, new CarrierMovementId("C003"), null, null);
42
+
43
+    /*
44
+      Cargo should not be misdirected at this point.
45
+     */
46
+    assertFalse(cargo.isMisdirected());
47
+
48
+    /*
49
+      An unexpected event occur.
50
+     */
51
+    handlingEventService.register(null, null, null, null, null);
52
+
53
+    /*
54
+      Now the cargo is misdirected.
55
+     */
56
+    assertTrue(cargo.isMisdirected());
57
+  }
58
+
59
+  public void testRun() throws Exception
60
+  {
61
+    
62
+  }
63
+
64
+
65
+  private Itinerary stubbedItinerarySelection(Set<Itinerary> itineraryCandidates) {
66
+    return itineraryCandidates.iterator().next();
67
+  }
68
+
69
+}

+ 55
- 69
dddsample/src/test/java/se/citerus/dddsample/service/RoutingServiceTest.java 파일 보기

@@ -1,69 +1,55 @@
1
-package se.citerus.dddsample.service;
2
-
3
-import junit.framework.TestCase;
4
-import se.citerus.dddsample.domain.*;
5
-import se.citerus.dddsample.repository.CargoRepository;
6
-
7
-import java.util.Date;
8
-import java.util.Set;
9
-
10
-public class RoutingServiceTest extends TestCase {
11
-
12
-  RoutingService routingService;
13
-  CargoRepository cargoRepository;
14
-  HandlingEventService handlingEventService;
15
-
16
-  public void xtestCalculateRoute() throws Exception {
17
-
18
-    TrackingId trackingId = new TrackingId("XYZ123");
19
-    Cargo cargo = cargoRepository.find(trackingId);
20
-    Specification specification = null;
21
-
22
-    /*
23
-      The routing service calculates a number of possible routes that
24
-      satisfy the given specification (must arrive in three days, must not
25
-      cost more than $10,000 etc).
26
-     */
27
-    Set<Itinerary> itineraryCandidates = routingService.calculatePossibleRoutes(cargo, specification);
28
-
29
-    /*
30
-      Someone, or something, selects the most appropriate itinerary and
31
-      assigns that itinerary to the cargo.
32
-     */
33
-    Itinerary itinerary = stubbedItinerarySelection(itineraryCandidates);
34
-    cargo.setItinerary(itinerary);
35
-
36
-    /*
37
-      A number of events occur, all of which are according to plan
38
-     */
39
-    handlingEventService.register(new Date(), trackingId, new CarrierMovementId("A001"), new UnLocode("SE","STO"), null);
40
-    handlingEventService.register(new Date(), trackingId, new CarrierMovementId("B002"), null, null);
41
-    handlingEventService.register(new Date(), trackingId, new CarrierMovementId("C003"), null, null);
42
-
43
-    /*
44
-      Cargo should not be misdirected at this point.
45
-     */
46
-    assertFalse(cargo.isMisdirected());
47
-
48
-    /*
49
-      An unexpected event occur.
50
-     */
51
-    handlingEventService.register(null, null, null, null, null);
52
-
53
-    /*
54
-      Now the cargo is misdirected.
55
-     */
56
-    assertTrue(cargo.isMisdirected());
57
-  }
58
-
59
-  public void testRun() throws Exception
60
-  {
61
-    
62
-  }
63
-
64
-
65
-  private Itinerary stubbedItinerarySelection(Set<Itinerary> itineraryCandidates) {
66
-    return itineraryCandidates.iterator().next();
67
-  }
68
-
69
-}
1
+package se.citerus.dddsample.service;
2
+
3
+import junit.framework.TestCase;
4
+import static org.easymock.EasyMock.*;
5
+import se.citerus.dddsample.domain.*;
6
+import static se.citerus.dddsample.domain.SampleLocations.HELSINKI;
7
+import static se.citerus.dddsample.domain.SampleLocations.HONGKONG;
8
+import se.citerus.dddsample.repository.LocationRepository;
9
+
10
+import java.util.List;
11
+import java.util.Set;
12
+
13
+public class RoutingServiceTest extends TestCase {
14
+
15
+  RoutingServiceImpl routingService;
16
+  LocationRepository locationRepository;
17
+
18
+  protected void setUp() throws Exception {
19
+    routingService = new RoutingServiceImpl();
20
+    locationRepository = createMock(LocationRepository.class);
21
+    routingService.setLocationRepository(locationRepository);
22
+  }
23
+
24
+  public void testCalculatePossibleRoutes() {
25
+    expect(locationRepository.findAll()).andStubReturn(SampleLocations.getAll());
26
+    replay(locationRepository);
27
+
28
+    Cargo cargo = new Cargo(new TrackingId("ABC"), HONGKONG, HELSINKI);
29
+    Set<Itinerary> candidates = routingService.calculatePossibleRoutes(cargo, null);
30
+    assertNotNull(candidates);
31
+    
32
+    for (Itinerary itinerary : candidates) {
33
+      List<Leg> legs = itinerary.legs();
34
+      assertNotNull(legs);
35
+      assertFalse(legs.isEmpty());
36
+
37
+      // Cargo origin and start of first leg should match
38
+      Location firstLegStart = legs.get(0).from();
39
+      assertEquals(cargo.origin(), firstLegStart);
40
+
41
+      // Cargo final destination and last leg stop should match
42
+      Location lastLegStop = legs.get(legs.size() - 1).to();
43
+      assertEquals(cargo.finalDestination(), lastLegStop);
44
+
45
+      for (int i = 0; i < legs.size() - 1; i++) {
46
+        // Assert that all legs are conencted
47
+        assertEquals(legs.get(i).to(), legs.get(i + 1).from());
48
+      }
49
+    }
50
+
51
+
52
+    verify(locationRepository);
53
+  }
54
+
55
+}