From 1f4bc6b13a5526f07b73a6d81a804f37c6bb430f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Mantelet?= <gregory.mantelet@astro.unistra.fr> Date: Fri, 27 Jul 2018 20:04:05 +0200 Subject: [PATCH] [UWS,TAP] Fix the weekly log file rotation. When enabled, it was generating a file each minute on the day before the specified day of week. For instance: if the log rotation frequency was `W 1 0 0` (so, weekly on Sunday at 00:00). The rotation was performed on Saturday midnight. But, because of a bad index correction, the rotation kept going on the whole day of Saturday. Since the rotated file is suffixed by the timestamp with hours and minutes (no seconds), it actually generated a new log file for each minute of the saturday. Of course, each time the file contained only one line (or 2 with some luck)...which is pretty useless. --- src/uws/service/file/EventFrequency.java | 120 +++++++++++---------- test/uws/service/file/TestLogRotation.java | 44 +++++++- 2 files changed, 105 insertions(+), 59 deletions(-) diff --git a/src/uws/service/file/EventFrequency.java b/src/uws/service/file/EventFrequency.java index a2c11f9..e35d468 100644 --- a/src/uws/service/file/EventFrequency.java +++ b/src/uws/service/file/EventFrequency.java @@ -2,21 +2,21 @@ package uws.service.file; /* * This file is part of UWSLibrary. - * + * * UWSLibrary is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * UWSLibrary is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. - * - * Copyright 2014-2015 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), + * + * Copyright 2014-2018 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ @@ -32,9 +32,9 @@ import java.util.regex.MatchResult; /** * <p>Let interpret and computing a frequency.</p> - * + * * <h3>Frequency syntax</h3> - * + * * <p>The frequency is expressed as a string at initialization of this object. This string must respect the following syntax:</p> * <ul> * <li>'D' hh mm : daily schedule at hh:mm</li> @@ -45,12 +45,12 @@ import java.util.regex.MatchResult; * </ul> * <p><i>Where: hh = integer between 0 and 23, mm = integer between 0 and 59, dd (for 'W') = integer between 1 and 7 (1:sunday, 2:monday, ..., 7:saturday), * dd (for 'M') = integer between 1 and 31.</i></p> - * + * * <p><i><b>Warning:</b> * The frequency type is case sensitive! Then you should particularly pay attention at the case * when using the frequency types 'M' (monthly) and 'm' (every minute). * </i></p> - * + * * <p> * Parsing errors are not thrown but "resolved" silently. The "solution" depends of the error. * 2 cases of errors are considered: @@ -59,7 +59,7 @@ import java.util.regex.MatchResult; * <li><b>Frequency type mismatch:</b> It happens when the first character is not one of the expected (D, W, M, h, m). * That means: bad case (i.e. 'd' rather than 'D'), another character. * In this case, the frequency will be: <b>daily at 00:00</b>.</li> - * + * * <li><b>Parameter(s) missing or incorrect:</b> With the "daily" frequency ('D'), at least 2 parameters must be provided ; * 3 for "weekly" ('W') and "monthly" ('M') ; only 1 for "hourly" ('h') ; none for "every minute" ('m'). * This number of parameters is a minimum: only the n first parameters will be considered while @@ -68,7 +68,7 @@ import java.util.regex.MatchResult; * <b>all parameters will be set to their default value</b> * (which is 0 for all parameter except dd for which it is 1).</li> * </ul> - * + * * <p>Examples:</p> * <ul> * <li><i>"" or NULL</i> = every day at 00:00</li> @@ -81,28 +81,28 @@ import java.util.regex.MatchResult; * <li><i>"M 32 6 30"</i> = every month on the 1st at 00:00, because with 'M' dd must respect the rule: 1 ≤ dd ≤ 31</li> * <li><i>"M 5 6 30 12"</i> = every month on the 5th at 06:30, because at least 3 parameters are expected and so considered: "12" and eventual other parameters are ignored</li> * </ul> - * + * * <h3>Computing next event date</h3> - * + * * <p> * When this class is initialized with a frequency, it is able to compute the date of the event following a given date. * The functions {@link #nextEvent()} and {@link #nextEvent(Date)} will compute this next event date * from, respectively, now (current date/time) and the given date (the date of the last event). Both are computing the date of the next * event by "adding" the frequency to the given date. And finally, the computed date is stored and returned. * </p> - * + * * <p>Then, you have 2 possibilities to trigger the desired event:</p> * <ul> * <li>By calling {@link #isTimeElapsed()}, you can test whether at the current moment the date of the next event has been reached or not. * In function of the value returned by this function you will be then able to process the desired action or not.</li> * <li>By creating a Timer with the next date event. Thus, the desired action will be automatically triggered at the exact moment.</li> * </p> - * - * + * + * * @author Marc Wenger (CDS) - * @author Grégory Mantelet (ARI) - * @version 4.1 (02/2015) - * @since 4.1 + * @author Grégory Mantelet (ARI;CDS) + * @version 4.4 (07/2018) + * @since 4.4 */ public final class EventFrequency { @@ -113,10 +113,10 @@ public final class EventFrequency { private static final DateFormat EVENT_ID_FORMAT = new SimpleDateFormat("yyyyMMdd_HHmm"); /** Ordered list of all week days (there, the first week day is Sunday). */ - private static final String[] WEEK_DAYS = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}; + private static final String[] WEEK_DAYS = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; /** Ordinal day number suffix (1<b>st</b>, 2<b>nd</b>, 3<b>rd</b> and <b>th</b> for the others). */ - private static final String[] DAY_SUFFIX = {"st","nd","rd","th"}; + private static final String[] DAY_SUFFIX = { "st", "nd", "rd", "th" }; /** Frequency type (D, W, M, h, m). Default value: 'D' */ private char dwm = 'D'; @@ -136,7 +136,7 @@ public final class EventFrequency { /** * <p>Create a new event frequency.</p> - * + * * <p>The frequency string must respect the following syntax:</p> * <ul> * <li>'D' hh mm : daily schedule at hh:mm</li> @@ -147,12 +147,12 @@ public final class EventFrequency { * </ul> * <p><i>Where: hh = integer between 0 and 23, mm = integer between 0 and 59, dd (for 'W') = integer between 1 and 7 (1:sunday, 2:monday, ..., 7:saturday), * dd (for 'M') = integer between 1 and 31.</i></p> - * + * * <p><i><b>Warning:</b> * The frequency type is case sensitive! Then you should particularly pay attention at the case * when using the frequency types 'M' (monthly) and 'm' (every minute). * </i></p> - * + * * <p> * Parsing errors are not thrown but "resolved" silently. The "solution" depends of the error. * 2 cases of errors are considered: @@ -161,7 +161,7 @@ public final class EventFrequency { * <li><b>Frequency type mismatch:</b> It happens when the first character is not one of the expected (D, W, M, h, m). * That means: bad case (i.e. 'd' rather than 'D'), another character. * In this case, the frequency will be: <b>daily at 00:00</b>.</li> - * + * * <li><b>Parameter(s) missing or incorrect:</b> With the "daily" frequency ('D'), at least 2 parameters must be provided ; * 3 for "weekly" ('W') and "monthly" ('M') ; only 1 for "hourly" ('h') ; none for "every minute" ('m'). * This number of parameters is a minimum: only the n first parameters will be considered while @@ -170,7 +170,7 @@ public final class EventFrequency { * <b>all parameters will be set to their default value</b> * (which is 0 for all parameter except dd for which it is 1).</li> * </ul> - * + * * <p>Examples:</p> * <ul> * <li><i>"" or NULL</i> = every day at 00:00</li> @@ -183,7 +183,7 @@ public final class EventFrequency { * <li><i>"M 32 6 30"</i> = every month on the 1st at 00:00, because with 'M' dd must respect the rule: 1 ≤ dd ≤ 31</li> * <li><i>"M 5 6 30 12"</i> = every month on the 5th at 06:30, because at least 3 parameters are expected and so considered: "12" and eventual other parameters are ignored</li> * </ul> - * + * * @param interval A string defining the event frequency (see above for the string format). */ public EventFrequency(String interval){ @@ -211,7 +211,7 @@ public final class EventFrequency { // Extract the parameters in function of the frequency type: switch(dwm){ - // CASE: DAILY + // CASE: DAILY case 'D': scan.findInLine("(\\d{1,2}) (\\d{1,2})"); try{ @@ -266,11 +266,11 @@ public final class EventFrequency { /** * Parse a string representing the day of the week (as a number). - * + * * @param dayNbStr String containing an integer representing a week day. - * + * * @return The identified week day. (integer between 0 and 6 (included)) - * + * * @throws IllegalStateException If the given string does not contain an integer or is not between 1 and 7 (included). */ private int parseDayOfWeek(final String dayNbStr) throws IllegalStateException{ @@ -278,17 +278,18 @@ public final class EventFrequency { int d = Integer.parseInt(dayNbStr); if (d >= 1 && d <= WEEK_DAYS.length) return d - 1; - }catch(Exception e){} + }catch(Exception e){ + } throw new IllegalStateException("Incorrect day of week (" + dayNbStr + ") ; it should be between 1 and 7 (both included)!"); } /** * Parse a string representing the day of the month. - * + * * @param dayStr String containing an integer representing a month day. - * + * * @return The identified month day. (integer between 1 and 31 (included)) - * + * * @throws IllegalStateException If the given string does not contain an integer or is not between 1 and 31 (included). */ private int parseDayOfMonth(final String dayStr) throws IllegalStateException{ @@ -296,17 +297,18 @@ public final class EventFrequency { int d = Integer.parseInt(dayStr); if (d >= 1 && d <= 31) return d; - }catch(Exception e){} + }catch(Exception e){ + } throw new IllegalStateException("Incorrect day of month (" + dayStr + ") ; it should be between 1 and 31 (both included)!"); } /** * Parse a string representing the hour part of a time (<b>hh</b>:mm). - * + * * @param hourStr String containing an integer representing an hour. - * + * * @return The identified hour. (integer between 0 and 23 (included)) - * + * * @throws IllegalStateException If the given string does not contain an integer or is not between 0 and 23 (included). */ private int parseHour(final String hourStr) throws IllegalStateException{ @@ -314,17 +316,18 @@ public final class EventFrequency { int h = Integer.parseInt(hourStr); if (h >= 0 && h <= 23) return h; - }catch(Exception e){} + }catch(Exception e){ + } throw new IllegalStateException("Incorrect hour number(" + hourStr + ") ; it should be between 0 and 23 (both included)!"); } /** * Parse a string representing the minute part of a time (hh:<b>mm</b>). - * + * * @param minStr String containing an integer representing a minute. - * + * * @return The identified minute. (integer between 0 and 59 (included)) - * + * * @throws IllegalStateException If the given string does not contain an integer or is not between 0 and 59 (included). */ private int parseMinute(final String minStr) throws IllegalStateException{ @@ -332,13 +335,14 @@ public final class EventFrequency { int m = Integer.parseInt(minStr); if (m >= 0 && m <= 59) return m; - }catch(Exception e){} + }catch(Exception e){ + } throw new IllegalStateException("Incorrect minute number (" + minStr + ") ; it should be between 0 and 59 (both included)!"); } /** * Tell whether the interval between the last event and now is greater or equals to the frequency represented by this object. - * + * * @return <i>true</i> if the next event date has been reached, <i>false</i> otherwise. */ public boolean isTimeElapsed(){ @@ -347,7 +351,7 @@ public final class EventFrequency { /** * Get the date of the next event. - * + * * @return Date of the next event, or NULL if no date has yet been computed. */ public Date getNextEvent(){ @@ -356,9 +360,9 @@ public final class EventFrequency { /** * <p>Get a string which identity the period between the last event and the next one (whose the date has been computed by this object).</p> - * + * * <p>This ID is built by formatting in string the given date of the last event.</p> - * + * * @return ID of the period before the next event. */ public String getEventID(){ @@ -367,18 +371,18 @@ public final class EventFrequency { /** * <p>Compute the date of the event, by adding the interval represented by this object to the current date/time.</p> - * + * * <p> * The role of this function is to compute the next event date, not to get it. After computation, you can get this date * thanks to {@link #getNextEvent()}. Furthermore, using {@link #isTimeElapsed()} after having called this function will * let you test whether the next event should (have) occur(red). * </p> - * + * * <p><i>Note: * This function computes the next event date by taking the current date as the date of the last event. However, * if the last event occurred at a different date, you should use {@link #nextEvent(Date)}. * </i></p> - * + * * @return Date at which the next event should occur. (basically, it is: NOW + frequency) */ public Date nextEvent(){ @@ -387,13 +391,13 @@ public final class EventFrequency { /** * <p>Compute the date of the event, by adding the interval represented by this object to the given date/time.</p> - * + * * <p> * The role of this function is to compute the next event date, not to get it. After computation, you can get this date * thanks to {@link #getNextEvent()}. Furthermore, using {@link #isTimeElapsed()} after having called this function will * let you test whether the next event should (have) occur(red). * </p> - * + * * @return Date at which the next event should occur. (basically, it is lastEventDate + frequency) */ public Date nextEvent(final Date lastEventDate){ @@ -403,7 +407,7 @@ public final class EventFrequency { // Compute the date of the next event: switch(dwm){ - // CASE: DAILY + // CASE: DAILY case 'D': date.add(Calendar.DAY_OF_YEAR, 1); date.set(Calendar.HOUR_OF_DAY, hour); @@ -414,7 +418,7 @@ public final class EventFrequency { // CASE: WEEKLY case 'W': // find the next right day to trigger the rotation - int weekday = date.get(Calendar.DAY_OF_WEEK); // sunday=1, ... saturday=7 + int weekday = date.get(Calendar.DAY_OF_WEEK) - 1; // sunday=0, ... saturday=6 if (weekday == day){ date.add(Calendar.WEEK_OF_YEAR, 1); }else{ @@ -451,7 +455,7 @@ public final class EventFrequency { date.set(Calendar.SECOND, 0); break; - /* OTHERWISE, the next event date is the given date! */ + /* OTHERWISE, the next event date is the given date! */ } // Save it in millisecond for afterward comparison with the current time (so that telling whether the time is elapsed or not): @@ -466,7 +470,7 @@ public final class EventFrequency { /** * Display in a human readable way the frequency represented by this object. - * + * * @return a string, i.e. weekly on Sunday at HH:MM */ @Override @@ -478,7 +482,7 @@ public final class EventFrequency { str.append(" at ").append(NN.format(hour)).append(':').append(NN.format(min)); break; case 'W': - str.append("weekly on ").append(WEEK_DAYS[day % 7]); + str.append("weekly on ").append(WEEK_DAYS[day % 6]); str.append(" at ").append(NN.format(hour)).append(':').append(NN.format(min)); break; case 'M': diff --git a/test/uws/service/file/TestLogRotation.java b/test/uws/service/file/TestLogRotation.java index 16aa1e4..566415a 100644 --- a/test/uws/service/file/TestLogRotation.java +++ b/test/uws/service/file/TestLogRotation.java @@ -8,6 +8,9 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; import org.junit.Test; @@ -231,7 +234,7 @@ public class TestLogRotation { for(int i = 0; i < 5; i++){ final int logFreq = i + 1; nbExpectedMessages += 30 / logFreq; - (new Thread(new Runnable(){ + (new Thread(new Runnable() { @Override public void run(){ try{ @@ -274,4 +277,43 @@ public class TestLogRotation { } } + @Test + public void testNextEvent(){ + // Event = weekly on Sunday at midnight + EventFrequency event = new EventFrequency("W 1 0 0"); + assertEquals("weekly on Sunday at 00:00", event.toString()); + + /* CASE: Today = Friday */ + + // Reference date for the test: Friday 27th July 2018 at noon + GregorianCalendar date = new GregorianCalendar(2018, Calendar.JULY, 27, 12, 0); + assertEquals(Calendar.FRIDAY, date.get(Calendar.DAY_OF_WEEK)); + + // Get the next event from the given date: + Date nextEvent = event.nextEvent(date.getTime()); + GregorianCalendar expectedNextEvent = new GregorianCalendar(2018, Calendar.JULY, 29, 0, 0); + assertEquals(expectedNextEvent.getTime().getTime(), nextEvent.getTime()); + + /* CASE: Today = Saturday */ + + // Reference date for the test: Saturday 28th July 2018 at noon + date = new GregorianCalendar(2018, Calendar.JULY, 28, 12, 0); + assertEquals(Calendar.SATURDAY, date.get(Calendar.DAY_OF_WEEK)); + + // Get the next event from the given date: + nextEvent = event.nextEvent(date.getTime()); + assertEquals(expectedNextEvent.getTime().getTime(), nextEvent.getTime()); + + /* CASE: Today = Sunday */ + + // Reference date for the test: Sunday 28th July 2018 at noon + date = new GregorianCalendar(2018, Calendar.JULY, 29, 12, 0); + assertEquals(Calendar.SUNDAY, date.get(Calendar.DAY_OF_WEEK)); + + // Get the next event from the given date: + nextEvent = event.nextEvent(date.getTime()); + expectedNextEvent = new GregorianCalendar(2018, Calendar.AUGUST, 5, 0, 0); + assertEquals(expectedNextEvent.getTime().getTime(), nextEvent.getTime()); + } + } -- GitLab