001    package hirondelle.web4jtools.logview.downtime;
002    
003    import hirondelle.web4j.model.Check;
004    import hirondelle.web4j.model.ModelCtorException;
005    import hirondelle.web4j.model.ModelUtil;
006    import static hirondelle.web4j.util.Consts.FAILS;
007    import java.util.Date;
008    
009    /** 
010    * Model Object for application down time. 
011    * 
012    * <P>Here, 'down time' has a specific meaning. It is extracted from your <em>application</em> logs only, 
013    * and not from server logs. It is calculated by examining all the log files in your application's logging directory. 
014    * Both the last-modified date of the files themselves, and the date attached to the <em>first</em>
015    * log record in each log file are examined. 
016    * 
017    * <P>This mechanism assumes that a new log file is created when the application restarts. 
018    * 
019    * <P>If A and B are two consecutive log files, then a down time is calculated as : <br> 
020    * <tt>(time of first log record in B) - (last-modified date for A)</tt>
021    * 
022    * <P>This class is immutable, and makes defensive copies.
023    */
024    public final class DownTime {
025    
026      /**
027      * Full constructor.
028      *  
029      * @param aFrom Start of down time interval (required).
030      * @param aTo End of the down time interval (required).
031      */
032      public DownTime(Date aFrom, Date aTo) throws ModelCtorException {
033        //defensive copies of mutable fields.
034        fFrom = aFrom.getTime();
035        fTo = aTo.getTime();
036        validateState();
037      }
038      
039      public Date getFrom() { return new Date(fFrom); }  
040      public Date getTo() { return new Date(fTo); }
041      
042      /** Duration of the down time, in minutes.  */
043      public Long getDuration() {
044        long result = 0;
045        if ( fTo >= fFrom ) {
046          result = (fTo - fFrom)/(1000*60); 
047        }
048        return result;
049      }
050      
051      @Override public String toString(){
052        return ModelUtil.toStringFor(this);
053      }
054      
055      @Override public boolean equals(Object aThat){
056        Boolean result = ModelUtil.quickEquals(this, aThat);
057        if ( result == null ) {
058          DownTime that = (DownTime)aThat;
059          result = ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
060        }
061        return result;
062      }
063      
064      @Override public int hashCode(){
065        if ( fHashCode == 0 ) {
066          fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
067        }
068        return fHashCode;
069      }
070    
071      // PRIVATE //
072      private final Long fFrom;
073      private final Long fTo;
074      private int fHashCode;
075    
076      private void validateState() throws ModelCtorException {
077        ModelCtorException ex = new ModelCtorException();
078        
079        if ( FAILS == Check.required(fFrom) ) {
080          ex.add("From is required.");
081        }
082        if ( FAILS == Check.optional(fTo) ) {
083          ex.add("To is required.");
084        }
085    
086        if ( ! ex.isEmpty() ) throw ex;
087      }
088      
089      private Object[] getSignificantFields(){
090        return new Object[] {fFrom, fTo};
091      }
092    }