- Added programmatic access to BuildID and Version through methods on FModel.getBuildInfo().

- Added class forge.model.BuildInfo with unit test.
- Added unit test for forge.model.FModel.
- Added exception class forge.model.MultipleForgeJarsFoundError.
- Updated forge.view.swing.Main with some preliminary code for progress monitoring.
- Reduced CheckStyle, FindBugs, and PMD violations in these files and in net.slightlymagic.braids.util.testng.BraidsAssertFunctions.
- Added missing package-info in net.slightlymagic.braids.util.testng.
This commit is contained in:
Braids
2011-08-12 14:47:37 +00:00
parent d8c7e407c5
commit 10431c521e
9 changed files with 609 additions and 37 deletions

View File

@@ -0,0 +1,234 @@
package forge.model;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Provides access to information about the current version and build ID.
*/
public class BuildInfo {
/** Exception-free means of getting the ASCII Charset. */
public static final Charset US_ASCII_CHARSET = Charset.forName("US-ASCII");
/** Convenience for file.separator. */
private static final String FILE_SEP = System.getProperty("file.separator");
/** Convenience for path.separator. */
private static final String PATH_SEP = System.getProperty("path.separator");
private static final Pattern FORGE_JAR_REGEX_2G = // NOPMD by Braids on 8/12/11 10:18 AM
Pattern.compile("^(.*" + Pattern.quote(FILE_SEP) + ")?"
+ Pattern.quote("forge-")
+ "([^" + Pattern.quote(FILE_SEP) + Pattern.quote(PATH_SEP) + "]*)"
+ Pattern.quote("-with-dependencies.jar") + "$",
Pattern.CASE_INSENSITIVE);
private transient String pathToForgeJar;
/**
* Construct a standard BuildInfo object.
*
* Package access is intentional for unit testing.
*
* @see FModel.getBuildInfo()
*/
BuildInfo() {
// empty
}
/**
* Unit-testable constructor which allows a specific jar file to be set.
*
* Dependency injection! Relax, this won't hurt a bit.
*
* @param pathToMockJarFile where to find the mock Forge jar
*/
public BuildInfo(final String pathToMockJarFile) {
pathToForgeJar = pathToMockJarFile;
}
/**
* Get the current build ID for Forge.
*
* @return a String representing the build identifier, or null if we could
* not determine the value.
*/
public final String getBuildID() {
String manifestResult;
String result;
try {
manifestResult = getManifestAttribute("Implementation-Build");
} catch (IOException exn1) {
manifestResult = null; // NOPMD by Braids on 8/12/11 10:21 AM
}
if (manifestResult == null) {
// Try getting the SVN version number by running the svnversion
// command. This is a long shot, but it works on some developers'
// systems.
Process proc = null;
BufferedReader reader = null;
try {
proc = Runtime.getRuntime().exec("svnversion");
final InputStream procStdoutStream = proc.getInputStream();
final Reader procReader = new InputStreamReader(procStdoutStream, US_ASCII_CHARSET);
reader = new BufferedReader(procReader);
result = reader.readLine(); // may be null
} catch (IOException exn2) {
result = null; // NOPMD by Braids on 8/12/11 10:21 AM
} finally {
try {
reader.close();
} catch (Throwable exn3) { // NOPMD by Braids on 8/12/11 10:21 AM
// ignored
}
try {
proc.destroy();
} catch (Throwable exn4) { // NOPMD by Braids on 8/12/11 10:21 AM
// ignored
}
}
}
else {
result = manifestResult;
}
return result;
}
/**
* Get the current version of Forge.
*
* @return a String representing the version specifier, or "SVN" if
* unknown.
*/
public final String getVersion() {
String manifestResult;
String result;
try {
manifestResult = getManifestAttribute("Implementation-Version");
} catch (IOException exn) {
manifestResult = null; // NOPMD by Braids on 8/12/11 10:21 AM
}
if (manifestResult == null) {
result = "SVN";
}
else {
result = manifestResult;
}
return result;
}
/**
* Fetch an attribute from the Forge main jar's manifest.
*
* @param manifestAttrName the name of the attribute you want from the manifest
* @return the attribute's value, which may be empty or null
* @throws IOException if a (unique) Forge jar could not be found
*/
protected final String getManifestAttribute(final String manifestAttrName) throws IOException {
String result = null;
JarFile jar = null;
InputStream manifestStream = null;
try {
if (pathToForgeJar == null) {
// We're definitely not unit testing. Try to load from the
// currently running jar.
manifestStream = ClassLoader.getSystemResourceAsStream("META-INF/MANIFEST.MF");
final Manifest manifest = new Manifest(manifestStream);
result = getMainManifestAttribute(manifest, manifestAttrName);
}
if (result == null && pathToForgeJar == null) {
// Try to find a unique Forge jar in the class path.
final String classPath = System.getProperty("java.class.path");
final String[] paths = classPath.split(PATH_SEP);
for (String path : paths) {
final Matcher matcher = FORGE_JAR_REGEX_2G.matcher(path);
if (matcher.matches()) {
if (pathToForgeJar == null) {
pathToForgeJar = path;
}
else {
// Error: we found more than one.
pathToForgeJar = null; // NOPMD by Braids on 8/12/11 10:21 AM
throw new MultipleForgeJarsFoundError(
"Classpath = " + System.getProperty("java.class.path"));
}
}
}
}
if (result == null && pathToForgeJar == null) {
throw new FileNotFoundException(
"There is nothing matching forge-*-with-dependencies.jar in the class path.");
}
if (result == null) {
jar = new JarFile(pathToForgeJar);
final Manifest manifest = jar.getManifest();
if (manifest == null) {
throw new IOException("Forge jar at <<" + pathToForgeJar + ">> has no manifest.");
}
result = getMainManifestAttribute(manifest, manifestAttrName);
}
}
finally {
try {
manifestStream.close();
} catch (Throwable ignored) { // NOPMD by Braids on 8/12/11 10:21 AM
// ignored
}
try {
jar.close();
} catch (Throwable ignored) { // NOPMD by Braids on 8/12/11 10:21 AM
// ignored
}
}
return result;
}
/**
* Convience method for fetching an attribute from the main section of a
* jar's manifest.
*
* @param manifest the manifest that provides attributes
* @param manifestAttrName the name of the attribute to fetch
* @return the value of the attribute, or null if not set
*/
protected final String getMainManifestAttribute(final Manifest manifest, final String manifestAttrName) {
final Attributes atts = manifest.getMainAttributes();
return atts.getValue(manifestAttrName);
}
}

View File

@@ -3,39 +3,107 @@ package forge.model;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
//import net.slightlymagic.braids.util.progress_monitor.BaseProgressMonitor;
import net.slightlymagic.braids.util.progress_monitor.BraidsProgressMonitor;
import arcane.util.MultiplexOutputStream;
/**
* The default Model implementation for Forge.
*
* This used to be an interface, but it seems unlikely that we will ever use a
* This used to be an interface, but it seems unlikely that we will ever use a
* different model.
*
* In case we need to convert it into an interface in the future, all fields of
* this class must be either private or public static final.
*/
public class FModel {
//private static final int NUM_INIT_PHASES = 1;
private final transient OutputStream logFileStream;
private final transient PrintStream oldSystemOut;
private final transient PrintStream oldSystemErr;
private BuildInfo buildInfo;
/**
* Constructor.
*
* @param theMonitor a progress monitor (from the View) that shows the
* progress of the model's initialization.
*
* @throws FileNotFoundException if we could not find or write to the log file.
*/
public FModel() throws FileNotFoundException {
public FModel(final BraidsProgressMonitor theMonitor) throws FileNotFoundException {
/* To be implemented later. -Braids
BraidsProgressMonitor monitor;
if (theMonitor == null) {
monitor = new BaseProgressMonitor(NUM_INIT_PHASES, 1);
}
else {
monitor = theMonitor;
}
*/
final File logFile = new File("forge.log");
final boolean deleteSucceeded = logFile.delete();
if (logFile.exists() && !deleteSucceeded) {
if (logFile.exists() && !deleteSucceeded && logFile.length() != 0) {
throw new IllegalStateException("Could not delete existing logFile:" + logFile.getAbsolutePath());
}
// This used to be a BufferedOutputStream, but that seems inappropriate for a log. --Braids
final OutputStream logFileStream = new FileOutputStream(logFile);
logFileStream = new FileOutputStream(logFile);
oldSystemOut = System.out;
System.setOut(new PrintStream(new MultiplexOutputStream(System.out, logFileStream), true));
oldSystemErr = System.err;
System.setErr(new PrintStream(new MultiplexOutputStream(System.err, logFileStream), true));
setBuildInfo(new BuildInfo());
}
/**
* Destructor for FModel.
* @throws Throwable indirectly
*/
@Override
protected final void finalize() throws Throwable {
close();
super.finalize();
}
/**
* Opposite of constructor; resets all system resources and closes the
* log file.
*/
public final void close() {
System.setOut(oldSystemOut);
System.setErr(oldSystemErr);
try {
logFileStream.close();
} catch (IOException e) { // NOPMD by Braids on 8/12/11 10:25 AM
// ignored
}
}
/**
* Getter for buildInfo.
*
* @return the buildInfo
*/
public final BuildInfo getBuildInfo() {
return buildInfo;
}
/**
* Setter for buildInfo.
*
* @param neoBuildInfo the buildInfo to set
*/
protected final void setBuildInfo(final BuildInfo neoBuildInfo) {
this.buildInfo = neoBuildInfo;
}
}

View File

@@ -0,0 +1,23 @@
package forge.model;
//import java.io.IOException;
/**
* Exception thrown by model when it is trying to find a single forge jar, but
* it finds more than one.
*/
public class MultipleForgeJarsFoundError extends RuntimeException {
/** Automatically generated. */
private static final long serialVersionUID = 8899307272033517172L;
/**
* Create an exception with a message.
*
* @param message the message, which could be the System's class path.
*/
public MultipleForgeJarsFoundError(final String message) {
super(message);
}
}

View File

@@ -42,7 +42,7 @@ public final class Main {
public static void main(final String[] args) {
ExceptionHandler.registerErrorHandling();
try {
final FModel model = new FModel();
final FModel model = new FModel(null);
Singletons.setModel(model);
final FView view = new ApplicationView();
Singletons.setView(view);