View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.rolling.helper;
15  
16  import java.text.SimpleDateFormat;
17  import java.util.Calendar;
18  import java.util.Date;
19  import java.util.GregorianCalendar;
20  import java.util.Locale;
21  import java.util.TimeZone;
22  
23  import ch.qos.logback.core.CoreConstants;
24  import ch.qos.logback.core.spi.ContextAwareBase;
25  
26  /**
27   * RollingCalendar is a helper class to
28   * {@link ch.qos.logback.core.rolling.TimeBasedRollingPolicy } or similar
29   * timed-based rolling policies. Given a periodicity type and the current time,
30   * it computes the start of the next interval (i.e. the triggering date).
31   *
32   * @author Ceki Gülcü
33   */
34  public class RollingCalendar extends GregorianCalendar {
35  
36    private static final long serialVersionUID = -5937537740925066161L;
37  
38    // The gmtTimeZone is used only in computeCheckPeriod() method.
39    static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT");
40  
41    PeriodicityType periodicityType = PeriodicityType.ERRONEOUS;
42  
43    public RollingCalendar() {
44      super();
45    }
46  
47    public RollingCalendar(TimeZone tz, Locale locale) {
48      super(tz, locale);
49    }
50  
51    public void init(String datePattern) {
52      periodicityType = computePeriodicityType(datePattern);
53    }
54  
55    private void setPeriodicityType(PeriodicityType periodicityType) {
56      this.periodicityType = periodicityType;
57    }
58  
59    public PeriodicityType getPeriodicityType() {
60      return periodicityType;
61    }
62  
63    public long getNextTriggeringMillis(Date now) {
64      return getNextTriggeringDate(now).getTime();
65    }
66  
67    // This method computes the roll over period by looping over the
68    // periods, starting with the shortest, and stopping when the r0 is
69    // different from from r1, where r0 is the epoch formatted according
70    // the datePattern (supplied by the user) and r1 is the
71    // epoch+nextMillis(i) formatted according to datePattern. All date
72    // formatting is done in GMT and not local format because the test
73    // logic is based on comparisons relative to 1970-01-01 00:00:00
74    // GMT (the epoch).
75    public PeriodicityType computePeriodicityType(String datePattern) {
76      RollingCalendar rollingCalendar = new RollingCalendar(GMT_TIMEZONE, Locale
77              .getDefault());
78  
79      // set sate to 1970-01-01 00:00:00 GMT
80      Date epoch = new Date(0);
81  
82      if (datePattern != null) {
83        for (PeriodicityType i : PeriodicityType.VALID_ORDERED_LIST) {
84          SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
85          simpleDateFormat.setTimeZone(GMT_TIMEZONE); // all date formatting done
86          // in GMT
87  
88          String r0 = simpleDateFormat.format(epoch);
89          rollingCalendar.setPeriodicityType(i);
90  
91          Date next = new Date(rollingCalendar.getNextTriggeringMillis(epoch));
92          String r1 = simpleDateFormat.format(next);
93  
94          // System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1);
95          if ((r0 != null) && (r1 != null) && !r0.equals(r1)) {
96            return i;
97          }
98        }
99      }
100     // we failed
101     return PeriodicityType.ERRONEOUS;
102   }
103 
104   public void printPeriodicity(ContextAwareBase cab) {
105     switch (periodicityType) {
106       case TOP_OF_MILLISECOND:
107         cab.addInfo("Roll-over every millisecond.");
108         break;
109 
110       case TOP_OF_SECOND:
111         cab.addInfo("Roll-over every second.");
112         break;
113 
114       case TOP_OF_MINUTE:
115         cab.addInfo("Roll-over every minute.");
116         break;
117 
118       case TOP_OF_HOUR:
119         cab.addInfo("Roll-over at the top of every hour.");
120         break;
121 
122       case HALF_DAY:
123         cab.addInfo("Roll-over at midday and midnight.");
124         break;
125 
126       case TOP_OF_DAY:
127         cab.addInfo("Roll-over at midnight.");
128         break;
129 
130       case TOP_OF_WEEK:
131         cab.addInfo("Rollover at the start of week.");
132         break;
133 
134       case TOP_OF_MONTH:
135         cab.addInfo("Rollover at start of every month.");
136         break;
137 
138       default:
139         cab.addInfo("Unknown periodicity.");
140     }
141   }
142 
143   public long periodsElapsed(long start, long end) {
144     if (start > end)
145       throw new IllegalArgumentException("Start cannot come before end");
146 
147     long diff = end - start;
148     switch (periodicityType) {
149 
150       case TOP_OF_MILLISECOND:
151         return diff;
152       case TOP_OF_SECOND:
153         return diff / CoreConstants.MILLIS_IN_ONE_SECOND;
154       case TOP_OF_MINUTE:
155         return diff / CoreConstants.MILLIS_IN_ONE_MINUTE;
156       case TOP_OF_HOUR:
157         return (int) diff / CoreConstants.MILLIS_IN_ONE_HOUR;
158       case TOP_OF_DAY:
159         return diff / CoreConstants.MILLIS_IN_ONE_DAY;
160       case TOP_OF_WEEK:
161         return diff / CoreConstants.MILLIS_IN_ONE_WEEK;
162       case TOP_OF_MONTH:
163         return diffInMonths(start, end);
164       default:
165         throw new IllegalStateException("Unknown periodicity type.");
166     }
167   }
168 
169   public static int diffInMonths(long startTime, long endTime) {
170     if (startTime > endTime)
171       throw new IllegalArgumentException("startTime cannot be larger than endTime");
172     Calendar startCal = Calendar.getInstance();
173     startCal.setTimeInMillis(startTime);
174     Calendar endCal = Calendar.getInstance();
175     endCal.setTimeInMillis(endTime);
176     int yearDiff = endCal.get(Calendar.YEAR) - startCal.get(Calendar.YEAR);
177     int monthDiff = endCal.get(Calendar.MONTH) - startCal.get(Calendar.MONTH);
178     return yearDiff * 12 + monthDiff;
179   }
180 
181   public Date getRelativeDate(Date now, int periods) {
182     this.setTime(now);
183 
184     switch (periodicityType) {
185       case TOP_OF_MILLISECOND:
186         this.add(Calendar.MILLISECOND, periods);
187         break;
188 
189       case TOP_OF_SECOND:
190         this.set(Calendar.MILLISECOND, 0);
191         this.add(Calendar.SECOND, periods);
192         break;
193 
194       case TOP_OF_MINUTE:
195         this.set(Calendar.SECOND, 0);
196         this.set(Calendar.MILLISECOND, 0);
197         this.add(Calendar.MINUTE, periods);
198         break;
199 
200       case TOP_OF_HOUR:
201         this.set(Calendar.MINUTE, 0);
202         this.set(Calendar.SECOND, 0);
203         this.set(Calendar.MILLISECOND, 0);
204         this.add(Calendar.HOUR_OF_DAY, periods);
205         break;
206 
207       case TOP_OF_DAY:
208         this.set(Calendar.HOUR_OF_DAY, 0);
209         this.set(Calendar.MINUTE, 0);
210         this.set(Calendar.SECOND, 0);
211         this.set(Calendar.MILLISECOND, 0);
212         this.add(Calendar.DATE, periods);
213         break;
214 
215       case TOP_OF_WEEK:
216         this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
217         this.set(Calendar.HOUR_OF_DAY, 0);
218         this.set(Calendar.MINUTE, 0);
219         this.set(Calendar.SECOND, 0);
220         this.set(Calendar.MILLISECOND, 0);
221         this.add(Calendar.WEEK_OF_YEAR, periods);
222         break;
223 
224       case TOP_OF_MONTH:
225         this.set(Calendar.DATE, 1);
226         this.set(Calendar.HOUR_OF_DAY, 0);
227         this.set(Calendar.MINUTE, 0);
228         this.set(Calendar.SECOND, 0);
229         this.set(Calendar.MILLISECOND, 0);
230         this.add(Calendar.MONTH, periods);
231         break;
232 
233       default:
234         throw new IllegalStateException("Unknown periodicity type.");
235     }
236 
237     return getTime();
238   }
239 
240   public Date getNextTriggeringDate(Date now) {
241     return getRelativeDate(now, 1);
242   }
243 }