ExceptionRecorder.java
package org.andromda.core.common;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;
/**
* <p>
* ExceptionRecorder provides a function to record an exception to a file along with the trace data if active. </p>
*
* @author Martin West
* @author Bob Fields
*/
public final class ExceptionRecorder
{
private static final Logger logger = Logger.getLogger(ExceptionRecorder.class);
/**
* File header constant
*/
static final String FILE_HEADER = "------- AndroMDA Exception Recording -------";
/**
* Run line system constant
*/
static final String RUN_SYSTEM = "Run System .....: ";
/**
* Run line jdk constant
*/
static final String RUN_JDK = "Run JDK ........: ";
/**
* Information not available constant
*/
static final String INFORMATION_UNAVAILABLE = " unavailable";
/**
* The exceptions directory name:exceptions.
*/
private static final String exceptionDirectoryName = ".";
/**
* The exceptions directory, initialized to exceptions.
*/
private static File exceptionDirectory = null;
private static final SimpleDateFormat cvDateFormat = new SimpleDateFormat("yyMMddHHmmss");
private static final Random random = new Random();
/**
* The shared instance.
*/
private static final ExceptionRecorder instance = new ExceptionRecorder();
/**
* Private constructor, this class is not intended to be instantiated.
*/
private ExceptionRecorder()
{
// Not intended to be instantiated
}
/**
* Gets the shared instance of the ExceptionRecorder.
*
* @return the shared ExceptionRecorder instance.
*/
public static ExceptionRecorder instance()
{
return instance;
}
/**
* <p>
* Writes out the exception to a file along with trace data if active. The file name is of the form sYYMMDDHHMMSS
* (_nn).exc where YY..SS is the timestamp (_nn) is an ascending sequence number when multiple exceptions occur in
* the same second. Returns the filename of the generated exception report. </p>
*
* @param throwable to record.
* @return record("", throwable, "S")
*/
public String record(Throwable throwable)
{
return record("", throwable, "S");
}
/**
* <p>
* Writes out the exception to a file along with trace data if active. The file name is of the form sYYMMDDHHMMSS
* (_nn).exc where YY..SS is the timestamp (_nn) is an ascending sequence number when multiple exceptions occur in
* the same second. Returns the filename of the generated exception report. </p>
*
* @param errorMessage to log with the exception report.
* @param throwable to record.
* @return record(errorMessage, throwable, "S")
*/
public String record(
String errorMessage,
Throwable throwable)
{
return record(errorMessage, throwable, "S");
}
/**
* The default prefix given, if prefix in {@link #record(String, Throwable, String) is null}.
*/
private static final String DEFAULT_PREFIX = "andromda";
/**
* <p>
* Writes out the exception to a file along with trace data if active. The file name is of the form sYYMMDDHHMMSS
* (_nn).exc where YY..SS is the timestamp <_nn>is an ascending sequence number when multiple exceptions occur in
* the same second. </p>
*
* @param message diagnostic message
* @param throwable exception to record.
* @param prefix for the file name.
* @return result string
*/
public String record(
String message,
Throwable throwable,
String prefix)
{
String result = null;
if (StringUtils.isEmpty(prefix))
{
prefix = DEFAULT_PREFIX;
}
try
{
final BuildInformation buildInformation = BuildInformation.instance();
final String uniqueName = getUniqueName(prefix);
final File exceptionFile = new File(exceptionDirectory, uniqueName);
result = exceptionFile.getCanonicalPath();
final PrintWriter writer = new PrintWriter(new FileWriter(exceptionFile));
writer.println(FILE_HEADER);
writer.println("Version ........: " + buildInformation.getBuildVersion());
writer.println("Error ..........: " + message);
writer.println("Build ..........: " + buildInformation.getBuildDate());
writer.println("Build System ...: " + buildInformation.getBuildSystem());
writer.println("Build JDK ......: " + buildInformation.getBuildJdk());
writer.println("Build Builder ..: " + buildInformation.getBuildBuilder());
// Place in try/catch in case system is protected.
try
{
writer.println(RUN_SYSTEM + System.getProperty("os.name") + System.getProperty("os.version"));
writer.println(RUN_JDK + System.getProperty("java.vm.vendor") + System.getProperty("java.vm.version"));
}
catch (Exception ex)
{
// ignore
writer.println(RUN_SYSTEM + INFORMATION_UNAVAILABLE);
writer.println(RUN_JDK + INFORMATION_UNAVAILABLE);
}
writer.println("Main Exception .: " + throwable.getMessage());
Throwable cause = ExceptionUtils.getRootCause(throwable);
if (cause == null)
{
cause = throwable;
}
writer.println("Root Exception .: " + cause);
cause.printStackTrace(writer);
writer.close();
AndroMDALogger.error("Exception recorded in --> '" + result + '\'');
}
catch (Throwable th)
{
final String errorMessage = "ExceptionRecorder.record error recording exception --> '" + throwable + '\'';
logger.error(errorMessage, th);
} // End catch
return result;
} // end of method record
/**
* The suffix to give the recorded exception files.
*/
private static final String SUFFIX = ".exc";
/**
* Gets a unique file name.
* @param prefix
* @return uniqueName
* @concurrency guarded
*/
protected synchronized String getUniqueName(String prefix)
{
String uniqueName = prefix + cvDateFormat.format(new Date()) + SUFFIX;
int suffix = 0;
File exceptionFile = new File(exceptionDirectory, uniqueName);
while (exceptionFile.exists())
{
uniqueName = prefix + cvDateFormat.format(new Date()) + '_' + suffix++ + SUFFIX;
exceptionFile = new File(exceptionDirectory, uniqueName);
// Give another user an opportunity to
// grab a file name. Use a random delay to
// introduce variability
try
{
Thread.sleep(Math.abs(random.nextInt() % 100));
}
catch (InterruptedException e1)
{
// ignore
}
} // end while
// Grab the file name, there is a window when we
// are writing the file, that some one else in
// a different VM could get the same file name.
try
{
RandomAccessFile file;
file = new RandomAccessFile(exceptionFile, "rw");
file.writeChar('t');
file.close();
}
catch (Exception ex)
{
// ignore
}
return uniqueName;
} // end method getUniqueName
static
{
/* initialize the exceptionDirectory */
try
{
exceptionDirectory = new File(exceptionDirectoryName);
if (!exceptionDirectory.exists())
{
exceptionDirectory.mkdir();
}
}
catch (Throwable th)
{
// ignore
}
finally
{
if (exceptionDirectory == null)
{
exceptionDirectory = new File(".");
}
}
}
/**
* Returns the directory to which the exceptions are written.
*
* @return the exception directory as a java.io.File instance.
*/
public File getExceptionDirectory()
{
return exceptionDirectory;
}
}