Skip to content

Commit

Permalink
command line usage msg and validation
Browse files Browse the repository at this point in the history
  • Loading branch information
eostermueller committed Dec 10, 2016
1 parent e642363 commit c8c9367
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github.eostermueller.heapspank.leakyspank.console;

public class CommandLineParameterException extends Exception {

private String proposedConfigClassName = null;
public CommandLineParameterException(String string) {
super(string);
}
public CommandLineParameterException(String string, Throwable t) {
super(string, t);
}
public String getProposedConfigClassName() {
return this.proposedConfigClassName;
}
public void setProposedConfigClassName(String val) {
this.proposedConfigClassName = val;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.github.eostermueller.heapspank.leakyspank.console;

import java.util.List;

public interface Config {

public abstract int getScreenRefreshIntervalSeconds();
Expand Down Expand Up @@ -27,4 +29,6 @@ public abstract void setScreenRefreshIntervalSeconds(

public abstract int getMaxIterations();

void setArgs(String[] args) throws CommandLineParameterException;

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.github.eostermueller.heapspank.leakyspank.console;


public class DefaultConfig implements Config {
private static final String DEFAULT_CONFIG_IMPL = "com.github.eostermueller.heapspank.leakyspank.console.DefaultConfig";
String viewClass = "com.github.eostermueller.heapspank.leakyspank.console.DefaultView";

@Override
public String getViewClass() {
return viewClass;
Expand Down Expand Up @@ -46,12 +49,68 @@ public long getPid() {
public void setPid(long pid) {
this.pid = pid;
}
public DefaultConfig(String[] args) {
this.pid = Long.parseLong(args[0]);
System.out.format("0: %s%n", args[0]);
this.setjMapHistoIntervalSeconds(5);//should be 15 for release
this.setjMapCountPerWindow(4);
this.setSuspectCountPerWindow(10);
/**
* Create a new instance of com.github.eostermueller.heapspank.leakyspank.console.Config
* @param args
* @return
* @throws CommandLineParameterException
*/
public static Config createNew(String[] args) throws CommandLineParameterException {
Config rc = null;
String proposedNameOfClass = getConfigClassName(args);
Object configInstance = null;

try {
Class c = Class.forName(proposedNameOfClass);
configInstance = c.newInstance();
} catch (Exception e) {
CommandLineParameterException x = new CommandLineParameterException("Unable to create [" + proposedNameOfClass + "]. Not in the classpath?", e);
x.setProposedConfigClassName(proposedNameOfClass);
throw x;
}

if (Config.class.isInstance(configInstance)) {
rc = (Config) configInstance;
rc.setArgs(args);
} else {
CommandLineParameterException x = new CommandLineParameterException("The -config class [" + proposedNameOfClass + "] must implement com.github.eostermueller.heapspank.leakyspank.console.Config");
x.setProposedConfigClassName(proposedNameOfClass);
throw x;
}

return rc;
}
private static String getConfigClassName(String[] args) throws CommandLineParameterException {
String rc = null;
for(int i = 0; i < args.length; i++) {
if (args[i].equals("-config")) {
if ( i+1 < args.length)
rc = args[i+1];
else {
CommandLineParameterException x = new CommandLineParameterException("parameter after -config must be name of a class that implements com.github.eostermueller.heapspank.leakyspank.console.Config");
x.setProposedConfigClassName(null);
throw x;
}
}
}
if (rc==null)
rc = DEFAULT_CONFIG_IMPL;
return rc;
}
@Override
public void setArgs(String[] args) throws CommandLineParameterException {

if (args.length >=1) {
this.pid = Long.parseLong(args[0]);
// System.out.format("0: %s%n", args[0]);
this.setjMapHistoIntervalSeconds(5);
this.setjMapCountPerWindow(4);
this.setSuspectCountPerWindow(10);
} else {
String error = "Add the pid of the java process you want to monitor for leaks.";
CommandLineParameterException e = new CommandLineParameterException(error);
throw e;
}
}
/* (non-Javadoc)
* @see com.github.eostermueller.heapspank.leakyspank.console.IConfig#getScreenRefreshIntervalSeconds()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.github.eostermueller.heapspank.leakyspank.console;

public class FifteenSecondJMapHistoInterval extends DefaultConfig {
@Override
public int getjMapHistoIntervalSeconds() {
return 15;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@

public class LeakySpankConsole implements DisplayUpdateListener {

private static final String VERSION = "v0.1";
private static final String BANNER_FORMAT = " %4ds leakySpank memory leak detector version [%s]%n";
private static final String BANNER_FORMAT_ALT = "# %4ds leakySpank memory leak detector version [%s] ##%n";
private static final String VERSION = "v0.6";
private static final String BANNER_FORMAT = " %4ds heapSpank memory leak detector version [%s]%n";
private static final String BANNER_FORMAT_ALT = "# %4ds heapSpank memory leak detector version [%s] ##%n";
private static final String INDENT = "\t";
Queue<Model> jmapHistoOutputQueue = new ConcurrentLinkedQueue<Model>();
JMapHistoRunner jMapHistoRunner = null;
LeakySpankContext leakySpankContext = null;
Expand All @@ -29,9 +30,44 @@ public class LeakySpankConsole implements DisplayUpdateListener {
public static void main(String args[]) throws InstantiationException,
IllegalAccessException, ClassNotFoundException {
LeakySpankConsole leakySpankConsole = new LeakySpankConsole();
Config config = new DefaultConfig(args);
leakySpankConsole.init(config);
leakySpankConsole.loopForever(leakySpankConsole.getConsoleView());
Config config;
try {
config = DefaultConfig.createNew(args);
if (config != null) {
leakySpankConsole.init(config);
leakySpankConsole.loopForever(leakySpankConsole.getConsoleView());
} else {
System.out.println("Fatal error. unable to create configuration.");
System.out.println( getUsage(args) );
}
} catch (CommandLineParameterException e) {
System.out.println("\n");
System.out.println(e.getMessage());
System.out.println( getUsage(args) );
}
}

private static String getUsage(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("\n");
sb.append("\n");
sb.append(INDENT + "------------------------------------------\n");
sb.append(INDENT + "Usage for heapSpank memory leak detection.\n");
sb.append("\n");
sb.append(INDENT + INDENT + "java -jar heapSpank.jar <myPid>\n");
sb.append("\n");
sb.append(INDENT + "OR\n");
sb.append("\n");
sb.append(INDENT + INDENT + "java -jar heapSpank.jar <myPid> -config <myConfig>\n");
sb.append("\n");
sb.append(INDENT + "WHERE\n");
sb.append("\n");
sb.append(INDENT + INDENT + "-- <myPid> is the process id (pid) of the java process to monitor for memory leaks.\n");
sb.append(INDENT + INDENT + " Run JAVA_HOME/bin/jps to display pids of all running JVMs.\n");
sb.append("\n");
sb.append(INDENT + INDENT + "-- <myConfig> is the full package and class name of a your custom class \n");
sb.append(INDENT + INDENT + " that implements com.github.eostermueller.heapspank.leakyspank.console.Config\n");
return sb.toString();
}

private void loopForever(ConsoleView view) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.github.eostermueller.heapspank.leakyspank;

import com.github.eostermueller.heapspank.leakyspank.console.Config;

public class TestConfig implements Config {

@Override
public int getScreenRefreshIntervalSeconds() {
// TODO Auto-generated method stub
return 99;
}

@Override
public void setScreenRefreshIntervalSeconds(int screenRefreshIntervalSeconds) {
// TODO Auto-generated method stub

}

@Override
public int getjMapHistoIntervalSeconds() {
// TODO Auto-generated method stub
return 0;
}

@Override
public void setjMapHistoIntervalSeconds(int jMapHistoIntervalSeconds) {
// TODO Auto-generated method stub

}

@Override
public void setjMapCountPerWindow(int jMapCountPerWindow) {
// TODO Auto-generated method stub

}

@Override
public int getjMapCountPerWindow() {
// TODO Auto-generated method stub
return 0;
}

@Override
public void setSuspectCountPerWindow(int suspectCountPerWindow) {
// TODO Auto-generated method stub

}

@Override
public int getSuspectCountPerWindow() {
// TODO Auto-generated method stub
return 0;
}

@Override
public long getPid() {
// TODO Auto-generated method stub
return 0;
}

@Override
public void setViewClass(String viewClass) {
// TODO Auto-generated method stub

}

@Override
public String getViewClass() {
// TODO Auto-generated method stub
return null;
}

@Override
public int getMaxIterations() {
// TODO Auto-generated method stub
return 0;
}

@Override
public void setArgs(String[] args) {
// TODO Auto-generated method stub

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.github.eostermueller.heapspank.leakyspank;

import static org.junit.Assert.*;

import org.junit.Test;

import com.github.eostermueller.heapspank.leakyspank.console.CommandLineParameterException;
import com.github.eostermueller.heapspank.leakyspank.console.Config;
import com.github.eostermueller.heapspank.leakyspank.console.DefaultConfig;

public class TestCustomConfigClass {

@Test
public void test() throws CommandLineParameterException {

String args[] = { "-config", "com.github.eostermueller.heapspank.leakyspank.TestConfig" };
Config testConfig = DefaultConfig.createNew(args);
assertEquals("Could not load test configuration class from classpath", 99, testConfig.getScreenRefreshIntervalSeconds());

}

@Test
public void testConfigClassNameNotInClasspath() {

String nameOfClassThatDoesNotExist = "com.github.eostermueller.heapspank.leakyspank.DoesNotExist";
String args[] = {
"-config",
nameOfClassThatDoesNotExist
};


try {
DefaultConfig.createNew(args);
fail("Should have thrown an exception because the class name after -config parm does not exist.");
} catch (CommandLineParameterException e) {
assertEquals(
"Could not find right message",
"Unable to create [" + e.getProposedConfigClassName() + "]. Not in the classpath?",
e.getMessage() );
assertEquals(nameOfClassThatDoesNotExist, e.getProposedConfigClassName());
assertEquals("did not find correct cause", ClassNotFoundException.class, e.getCause().getClass());
}

}
@Test
public void testMissingClassName() {

String args[] = {
"-config",
/* "com.github.eostermueller.heapspank.leakyspank.TestConfig" */
};

Config testConfig;
try {
testConfig = DefaultConfig.createNew(args);
fail("Should have thrown an exception because the class was missing as a command line argument.");
} catch (CommandLineParameterException e) {
assertEquals(
"Could not find right message",
"parameter after -config must be name of a class that implements com.github.eostermueller.heapspank.leakyspank.console.Config",
e.getMessage() );
assertNull(e.getProposedConfigClassName());
}
}
@Test
public void testClassNameWithoutCorrectImplementation() {


//The correct interface is com.github.eostermueller.heapspank.leakyspank.console.Config
String nameOfClassThatDoesNotImplementCorrectInterface = "com.github.eostermueller.heapspank.leakyspank.TestCustomConfigClass";
String args[] = {
"-config",
nameOfClassThatDoesNotImplementCorrectInterface
};

try {
Config testConfig = DefaultConfig.createNew(args);
fail("Should have thrown an exception because the class name after -config parm does not exist.");
} catch (CommandLineParameterException e) {
assertEquals(
"Could not find right message",
"The -config class [" + e.getProposedConfigClassName() + "] must implement com.github.eostermueller.heapspank.leakyspank.console.Config",
e.getMessage() );
assertEquals(nameOfClassThatDoesNotImplementCorrectInterface, e.getProposedConfigClassName());
assertNull("did not find correct cause", e.getCause());
}

}
}

0 comments on commit c8c9367

Please sign in to comment.