/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2008 Sun Microsystems, Inc.
* Portions Copyright 2014 ForgeRock AS
*/
/**
* A MultiFileTextWriter is a specialized TextWriter which supports publishing
* log records to a set of files. MultiFileWriters write to one file in the
* set at a time, switching files as is dictated by a specified rotation
* and retention policies.
*
* When a switch is required, the writer closes the current file and opens a
* new one named in accordance with a specified FileNamingPolicy.
*/
public class MultifileTextWriter
{
/**
* The tracer object for the debug logger.
*/
new CopyOnWriteArrayList<RotationPolicy>();
new CopyOnWriteArrayList<RetentionPolicy>();
//TODO: Implement actions.
private int bufferSize;
private boolean autoFlush;
private boolean append;
private long interval;
private boolean stopRequested;
/** The underlying output stream. */
/** The underlaying buffered writer using the output steram. */
/**
* Creates a new instance of MultiFileTextWriter with the supplied policies.
*
* @param name the name of the log rotation thread.
* @param interval the interval to check whether the logs need to be rotated.
* @param namingPolicy the file naming policy to use to name rotated log.
* files.
* @param filePermissions the file permissions to set on the log files.
* @param errorHandler the log publisher error handler to notify when
* an error occurs.
* @param encoding the encoding to use to write the log files.
* @param autoFlush whether to flush the writer on every println.
* @param append whether to append to an existing log file.
* @param bufferSize the bufferSize to use for the writer.
* @throws IOException if an error occurs while creating the log file.
* @throws DirectoryException if an error occurs while preping the new log
* file.
*/
boolean autoFlush,
boolean append,
int bufferSize)
throws IOException, DirectoryException
{
this.namingPolicy = namingPolicy;
this.filePermissions = filePermissions;
this.errorHandler = errorHandler;
this.encoding = UTF8_ENCODING;
this.bufferSize = bufferSize;
this.stopRequested = false;
rotaterThread = new RotaterThread(this);
}
/**
* Construct a PrintWriter for a file.
* @param file - the file to open for writing
* @param filePermissions - the file permissions to set on the file.
* @param encoding - the encoding to use when writing log records.
* @param append - indicates whether the file should be appended to or
* truncated.
* @param bufferSize - the buffer size to use for the writer.
* @throws IOException if the PrintWriter could not be constructed
* or if the file already exists and it was indicated this should be
* an error.
* @throws DirectoryException if there was a problem setting permissions on
* the file.
*/
int bufferSize)
throws IOException, DirectoryException
{
// Create new file if it doesn't exist
{
}
if(bufferSize <= 0)
{
}
else
{
}
// Try to apply file permissions.
{
try
{
{
}
}
catch(Exception e)
{
// Log an warning that the permissions were not set.
}
}
}
/**
* Add a rotation policy to enforce on the files written by this writer.
*
* @param policy The rotation policy to add.
*/
{
if(policy instanceof SizeBasedRotationPolicy)
{
if(sizeLimit == 0 ||
{
}
// Add this as a change listener so we can update the size limit.
}
}
/**
* Add a retention policy to enforce on the files written by this writer.
*
* @param policy The retention policy to add.
*/
{
}
/**
* Removes all the rotation policies currently enforced by this writer.
*/
public void removeAllRotationPolicies()
{
{
if(policy instanceof SizeBasedRotationPolicy)
{
sizeLimit = 0;
// Remove this as a change listener.
}
}
this.rotationPolicies.clear();
}
/**
* Removes all retention policies being enforced by this writer.
*/
public void removeAllRetentionPolicies()
{
this.retentionPolicies.clear();
}
/**
* Set the auto flush setting for this writer.
*
* @param autoFlush If the writer should flush the buffer after every line.
*/
{
}
/**
* Set the append setting for this writter.
*
* @param append If the writer should append to an existing file.
*/
{
}
/**
* Set the buffer size for this writter.
*
* @param bufferSize The size of the underlying output stream buffer.
*/
{
this.bufferSize = bufferSize;
}
/**
* Set the file permission to set for newly created log files.
*
* @param filePermissions The file permission to set for new log files.
*/
{
this.filePermissions = filePermissions;
}
/**
* Retrieves the current naming policy used to generate log file names.
*
* @return The current naming policy in use.
*/
{
return namingPolicy;
}
/**
* Set the naming policy to use when generating new log files.
*
* @param namingPolicy the naming policy to use to name log files.
*/
{
this.namingPolicy = namingPolicy;
}
/**
* Set the interval in which the rotator thread checks to see if the log
* file should be rotated.
*
* @param interval The interval to check if the log file needs to be rotated.
*/
{
// Wake up the thread if its sleeping on the old interval
{
}
}
/**
* {@inheritDoc}
*/
public boolean isConfigurationChangeAcceptable(
{
// This should always be ok
return true;
}
/**
* {@inheritDoc}
*/
{
// Go through all current size rotation policies and get the
// lowest size setting.
{
if(policy instanceof SizeBasedRotationPolicy)
{
{
{
}
}
else
{
{
}
}
}
}
}
/**
* A rotater thread is responsible for checking if the log files need to be
* rotated based on the policies. It will do so if necessary.
*/
{
/**
* Create a new rotater thread.
*/
{
super(name);
}
/**
* the run method of the rotaterThread. It wakes up periodically and checks
* whether the file needs to be rotated based on the rotation policy.
*/
public void run()
{
while(!isShuttingDown())
{
try
{
}
catch(InterruptedException e)
{
// We expect this to happen.
}
catch(Exception e)
{
if (debugEnabled())
{
}
}
{
{
rotate();
}
}
{
try
{
{
if(debugEnabled())
{
}
}
{
}
}
catch(DirectoryException de)
{
if(debugEnabled())
{
}
}
}
}
}
}
/**
* Retrieves the human-readable name for this shutdown listener.
*
* @return The human-readable name for this shutdown listener.
*/
{
return "MultifileTextWriter Thread " + name;
}
/**
* Indicates that the Directory Server has received a request to stop running
* and that this shutdown listener should take any action necessary to prepare
* for it.
*
* @param reason The human-readable reason for the shutdown.
*/
{
stopRequested = true;
// Wait for rotater to terminate
try {
// Interrupt if its sleeping
}
catch (InterruptedException ex) {
// Ignore; we gotta wait..
}
}
// Don't close the writer as there might still be message to be
// written. manually shutdown just before the server process
// exists.
}
/**
* Queries whether the publisher is in shutdown mode.
*
* @return if the publish is in shutdown mode.
*/
private boolean isShuttingDown()
{
return stopRequested;
}
/**
* Shutdown the text writer.
*/
public void shutdown()
{
try
{
}
catch(Exception e)
{
}
}
/**
* Write a log record string to the file.
*
* @param record the log record to write.
*/
{
// Assume each character is 1 byte ASCII
char c;
for (int i=0; i < length; i++)
{
if (c != (byte) (c & 0x0000007F))
{
try
{
// String contains a non ASCII character. Fall back to getBytes.
}
catch(Exception e)
{
}
break;
}
}
synchronized(this)
{
{
rotate();
}
try
{
}
catch(Exception e)
{
}
if(autoFlush)
{
flush();
}
}
}
/**
* {@inheritDoc}
*/
public void flush()
{
try
{
}
catch(Exception e)
{
}
}
/**
* Tries to rotate the log files. If the new log file already exists, it
* tries to rename the file. On failure, all subsequent log write requests
* will throw exceptions.
*/
public synchronized void rotate()
{
try
{
}
catch(Exception e)
{
if(debugEnabled())
{
}
}
try
{
}
catch (Exception e)
{
if(debugEnabled())
{
}
}
//RotationActionThread rotThread =
// new RotationActionThread(newFile, actions, configEntry);
//rotThread.start();
if(debugEnabled())
{
}
}
/**
* This method sets the actions that need to be executed after rotation.
*
* @param actions An array of actions that need to be executed on rotation.
*/
{
}
/** {@inheritDoc} */
public long getBytesWritten()
{
return outputStream.written;
}
/**
* Retrieves the last time one or more log files are cleaned in this instance
* of the Directory Server. If log files have never been cleaned, this value
* will be the time the server started.
*
* @return The last time log files are cleaned.
*/
{
return lastCleanTime;
}
/**
* Retrieves the number of files cleaned in the last cleanup run.
*
* @return The number of files cleaned int he last cleanup run.
*/
public long getLastCleanCount()
{
return lastCleanCount;
}
/** {@inheritDoc} */
{
return lastRotationTime;
}
/**
* Retrieves the total number file rotations occurred in this instance of the
* Directory Server.
*
* @return The total number of file rotations.
*/
public long getTotalFilesRotated()
{
return totalFilesRotated;
}
/**
* Retrieves the total number of files cleaned in this instance of the
* Directory Server.
*
* @return The total number of files cleaned.
*/
public long getTotalFilesCleaned()
{
return totalFilesCleaned;
}
}