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 }