View Javadoc

1   package se.citerus.dddsample.domain.model.handling;
2   
3   import org.apache.commons.lang.Validate;
4   import org.apache.commons.lang.builder.EqualsBuilder;
5   import org.apache.commons.lang.builder.HashCodeBuilder;
6   import se.citerus.dddsample.domain.model.cargo.Cargo;
7   import se.citerus.dddsample.domain.model.location.Location;
8   import se.citerus.dddsample.domain.model.voyage.Voyage;
9   import se.citerus.dddsample.domain.shared.DomainEvent;
10  import se.citerus.dddsample.domain.shared.DomainObjectUtils;
11  import se.citerus.dddsample.domain.shared.ValueObject;
12  
13  import java.util.Date;
14  
15  /**
16   * A HandlingEvent is used to register the event when, for instance,
17   * a cargo is unloaded from a carrier at a some loacation at a given time.
18   * <p/>
19   * The HandlingEvent's are sent from different Incident Logging Applications
20   * some time after the event occured and contain information about the
21   * {@link se.citerus.dddsample.domain.model.cargo.TrackingId}, {@link se.citerus.dddsample.domain.model.location.Location}, timestamp of the completion of the event,
22   * and possibly, if applicable a {@link se.citerus.dddsample.domain.model.voyage.Voyage}.
23   * <p/>
24   * This class is the only member, and consequently the root, of the HandlingEvent aggregate. 
25   * <p/>
26   * HandlingEvent's could contain information about a {@link Voyage} and if so,
27   * the event type must be either {@link Type#LOAD} or {@link Type#UNLOAD}.
28   * <p/>
29   * All other events must be of {@link Type#RECEIVE}, {@link Type#CLAIM} or {@link Type#CUSTOMS}.
30   */
31  public final class HandlingEvent implements DomainEvent<HandlingEvent> {
32  
33    private Type type;
34    private Voyage voyage;
35    private Location location;
36    private Date completionTime;
37    private Date registrationTime;
38    private Cargo cargo;
39  
40    /**
41     * Handling event type. Either requires or prohibits a carrier movement
42     * association, it's never optional.
43     */
44    public enum Type implements ValueObject<Type> {
45      LOAD(true),
46      UNLOAD(true),
47      RECEIVE(false),
48      CLAIM(false),
49      CUSTOMS(false);
50  
51      private final boolean voyageRequired;
52  
53      /**
54       * Private enum constructor.
55       *
56       * @param voyageRequired whether or not a voyage is associated with this event type
57       */
58      private Type(final boolean voyageRequired) {
59        this.voyageRequired = voyageRequired;
60      }
61  
62      /**
63       * @return True if a voyage association is required for this event type.
64       */
65      public boolean requiresVoyage() {
66        return voyageRequired;
67      }
68  
69      /**
70       * @return True if a voyage association is prohibited for this event type.
71       */
72      public boolean prohibitsVoyage() {
73        return !requiresVoyage();
74      }
75  
76      @Override
77      public boolean sameValueAs(Type other) {
78        return other != null && this.equals(other);
79      }
80  
81    }
82  
83    /**
84     * @param cargo            cargo
85     * @param completionTime   completion time, the reported time that the event actually happened (e.g. the receive took place).
86     * @param registrationTime registration time, the time the message is received
87     * @param type             type of event
88     * @param location         where the event took place
89     * @param voyage           the voyage
90     */
91    public HandlingEvent(final Cargo cargo,
92                         final Date completionTime,
93                         final Date registrationTime,
94                         final Type type,
95                         final Location location,
96                         final Voyage voyage) {
97      Validate.notNull(cargo, "Cargo is required");
98      Validate.notNull(completionTime, "Completion time is required");
99      Validate.notNull(registrationTime, "Registration time is required");
100     Validate.notNull(type, "Handling event type is required");
101     Validate.notNull(location, "Location is required");
102     Validate.notNull(voyage, "Voyage is required");
103 
104     if (type.prohibitsVoyage()) {
105       throw new IllegalArgumentException("Voyage is not allowed with event type " + type);
106     }
107 
108     this.voyage = voyage;
109     this.completionTime = (Date) completionTime.clone();
110     this.registrationTime = (Date) registrationTime.clone();
111     this.type = type;
112     this.location = location;
113     this.cargo = cargo;
114   }
115 
116   /**
117    * @param cargo            cargo
118    * @param completionTime   completion time, the reported time that the event actually happened (e.g. the receive took place).
119    * @param registrationTime registration time, the time the message is received
120    * @param type             type of event
121    * @param location         where the event took place
122    */
123   public HandlingEvent(final Cargo cargo,
124                        final Date completionTime,
125                        final Date registrationTime,
126                        final Type type,
127                        final Location location) {
128     Validate.notNull(cargo, "Cargo is required");
129     Validate.notNull(completionTime, "Completion time is required");
130     Validate.notNull(registrationTime, "Registration time is required");
131     Validate.notNull(type, "Handling event type is required");
132     Validate.notNull(location, "Location is required");
133 
134     if (type.requiresVoyage()) {
135       throw new IllegalArgumentException("Voyage is required for event type " + type);
136     }
137 
138     this.completionTime = (Date) completionTime.clone();
139     this.registrationTime = (Date) registrationTime.clone();
140     this.type = type;
141     this.location = location;
142     this.cargo = cargo;
143     this.voyage = null;
144   }
145 
146   public Type type() {
147     return this.type;
148   }
149 
150   public Voyage voyage() {
151     return DomainObjectUtils.nullSafe(this.voyage, Voyage.NONE);
152   }
153 
154   public Date completionTime() {
155     return new Date(this.completionTime.getTime());
156   }
157 
158   public Date registrationTime() {
159     return new Date(this.registrationTime.getTime());
160   }
161 
162   public Location location() {
163     return this.location;
164   }
165 
166   public Cargo cargo() {
167     return this.cargo;
168   }
169 
170   @Override
171   public boolean equals(final Object o) {
172     if (this == o) return true;
173     if (o == null || getClass() != o.getClass()) return false;
174 
175     final HandlingEvent event = (HandlingEvent) o;
176 
177     return sameEventAs(event);
178   }
179 
180   @Override
181   public boolean sameEventAs(final HandlingEvent other) {
182     return other != null && new EqualsBuilder().
183       append(this.cargo, other.cargo).
184       append(this.voyage, other.voyage).
185       append(this.completionTime, other.completionTime).
186       append(this.location, other.location).
187       append(this.type, other.type).
188       isEquals();
189   }
190 
191   @Override
192   public int hashCode() {
193     return new HashCodeBuilder().
194       append(cargo).
195       append(voyage).
196       append(completionTime).
197       append(location).
198       append(type).
199       toHashCode();
200   }
201 
202   @Override
203   public String toString() {
204     final StringBuilder builder = new StringBuilder("\n--- Handling event ---\n").
205       append("Cargo: ").append(cargo.trackingId()).append("\n").
206       append("Type: ").append(type).append("\n").
207       append("Location: ").append(location.name()).append("\n").
208       append("Completed on: ").append(completionTime).append("\n").
209       append("Registered on: ").append(registrationTime).append("\n");
210     
211     if (voyage != null) {
212       builder.append("Voyage: ").append(voyage.voyageNumber()).append("\n");
213     }
214 
215     return builder.toString();
216   }
217 
218   HandlingEvent() {
219     // Needed by Hibernate
220   }
221 
222 
223   // Auto-generated surrogate key
224   private Long id;
225 
226 }