/*
* JBoss, Home of Professional Open Source.
* Copyright 2015, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.controller.audit;
import org.jboss.as.controller.logging.ControllerLogger;
import org.jboss.as.controller.services.path.PathManagerService;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
/**
* All methods on this class should be called with {@link ManagedAuditLoggerImpl}'s lock taken.
*
* @author <a href="mailto:istudens@redhat.com">Ivo Studensky</a>
*/
public class PeriodicRotatingFileAuditLogHandler extends AbstractFileAuditLogHandler {
private SimpleDateFormat format;
private Period period = Period.NEVER;
private volatile String nextSuffix;
private volatile long nextRollover = Long.MAX_VALUE;
private TimeZone timeZone = TimeZone.getDefault();
private String suffix;
public PeriodicRotatingFileAuditLogHandler(final String name, final String formatterName, final int maxFailureCount, final PathManagerService pathManager, final String path, final String relativeTo, final String suffix, final TimeZone timeZone) {
super(name, formatterName, maxFailureCount, pathManager, path, relativeTo);
this.suffix = suffix; // remember the value just for the sake of the method isDifferent()
if (timeZone != null)
this.timeZone = timeZone; // needed for setSuffix in the next step
setSuffix(suffix);
}
@Override
protected void initializeAtStartup(final File file) {
final long lastModified = file.lastModified();
calcNextRollover((lastModified > 0) ? lastModified : System.currentTimeMillis());
}
@Override
protected void rotateLogFile(final File file) {
final long now = System.currentTimeMillis();
if (now >= nextRollover) {
rollOver(file);
calcNextRollover(now);
}
}
/**
* Set the suffix string. The string is in a format which can be understood by {@link java.text.SimpleDateFormat}.
* The period of the rotation is automatically calculated based on the suffix.
*
* @param suffix the suffix
* @throws IllegalArgumentException if the suffix is not valid
*/
private void setSuffix(String suffix) throws IllegalArgumentException {
// method code stolen from the logging subsystem
final SimpleDateFormat format = new SimpleDateFormat(suffix);
format.setTimeZone(timeZone);
final int len = suffix.length();
Period period = Period.NEVER;
for (int i = 0; i < len; i ++) {
switch (suffix.charAt(i)) {
case 'y': period = min(period, Period.YEAR); break;
case 'M': period = min(period, Period.MONTH); break;
case 'w':
case 'W': period = min(period, Period.WEEK); break;
case 'D':
case 'd':
case 'F':
case 'E': period = min(period, Period.DAY); break;
case 'a': period = min(period, Period.HALF_DAY); break;
case 'H':
case 'k':
case 'K':
case 'h': period = min(period, Period.HOUR); break;
case 'm': period = min(period, Period.MINUTE); break;
case '\'': while (suffix.charAt(++i) != '\''){} break;
case 's':
case 'S': throw new IllegalArgumentException("Rotating by second or millisecond is not supported");
}
}
this.format = format;
this.period = period;
}
private void rollOver(final File file) {
final File backup = new File(file.getParentFile(), file.getName() + nextSuffix);
try {
rename(file, backup);
} catch (IOException e) {
throw ControllerLogger.ROOT_LOGGER.couldNotBackUp(e, file.getAbsolutePath(), backup.getAbsolutePath());
}
createNewFile(file);
}
private void calcNextRollover(final long fromTime) {
// method code stolen from the logging subsystem
if (period == Period.NEVER) {
nextRollover = Long.MAX_VALUE;
return;
}
nextSuffix = format.format(new Date(fromTime));
final Calendar calendar = Calendar.getInstance(timeZone);
calendar.setTimeInMillis(fromTime);
final Period period = this.period;
// clear out less-significant fields
switch (period) {
default:
case YEAR:
calendar.set(Calendar.MONTH, 0);
case MONTH:
calendar.set(Calendar.DAY_OF_MONTH, 0);
calendar.clear(Calendar.WEEK_OF_MONTH);
case WEEK:
if (period == Period.WEEK) {
calendar.set(Calendar.DAY_OF_WEEK, 0);
} else {
calendar.clear(Calendar.DAY_OF_WEEK);
}
calendar.clear(Calendar.DAY_OF_WEEK_IN_MONTH);
case DAY:
calendar.set(Calendar.HOUR_OF_DAY, 0);
case HALF_DAY:
calendar.set(Calendar.HOUR, 0);
case HOUR:
calendar.set(Calendar.MINUTE, 0);
case MINUTE:
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
}
// increment the relevant field
switch (period) {
case YEAR:
calendar.add(Calendar.YEAR, 1);
break;
case MONTH:
calendar.add(Calendar.MONTH, 1);
break;
case WEEK:
calendar.add(Calendar.WEEK_OF_YEAR, 1);
break;
case DAY:
calendar.add(Calendar.DAY_OF_MONTH, 1);
break;
case HALF_DAY:
calendar.add(Calendar.AM_PM, 1);
break;
case HOUR:
calendar.add(Calendar.HOUR_OF_DAY, 1);
break;
case MINUTE:
calendar.add(Calendar.MINUTE, 1);
break;
}
nextRollover = calendar.getTimeInMillis();
}
@Override
boolean isDifferent(AuditLogHandler other) {
if (other instanceof PeriodicRotatingFileAuditLogHandler == false){
return true;
}
PeriodicRotatingFileAuditLogHandler otherHandler = (PeriodicRotatingFileAuditLogHandler)other;
if (!suffix.equals(otherHandler.suffix)) {
return true;
}
if (!timeZone.equals(otherHandler.timeZone)) {
return true;
}
if (super.isDifferent(other)) {
return true;
}
return false;
}
private static <T extends Comparable<? super T>> T min(T a, T b) {
return a.compareTo(b) <= 0 ? a : b;
}
/**
* Possible period values. Keep in strictly ascending order of magnitude.
*/
public enum Period {
MINUTE,
HOUR,
HALF_DAY,
DAY,
WEEK,
MONTH,
YEAR,
NEVER,
}
}