diff --git a/.classpath b/.classpath
new file mode 100644
index 00000000..9b6a52c8
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..e7e9d11d
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,2 @@
+# Default ignored files
+/workspace.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 00000000..0255334f
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 00000000..03e48d4b
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 00000000..b3e9cbd3
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..25d34a47
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 00000000..e96534fb
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..3701c312
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,21 @@
+language: java
+install: true
+
+os: linux
+dist: bionic
+jdk:
+- oraclejdk9
+- oraclejdk11
+
+before_cache:
+ - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
+ - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
+
+cache:
+ directories:
+ - $HOME/.gradle/caches/
+ - $HOME/.gradle/wrapper/
+
+script:
+ - ./gradlew check -s
+
diff --git a/README.md b/README.md
index 4c8cf55c..6c82f501 100644
--- a/README.md
+++ b/README.md
@@ -44,3 +44,10 @@ default: _appmap.yml_
`appmap.output.directory`
specify the output directory of `appmap.json` files
default: _./_
+
+
+# Build status
+
+[](https://travis-ci.org/applandinc/appmap-java)
+
+
diff --git a/build.gradle b/build.gradle
index d74c4eb0..78a8aab2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,22 +7,16 @@
*/
plugins {
- id 'application'
+ id 'java'
id 'com.github.johnrengelman.shadow' version '5.2.0'
}
-application {
- mainClassName = 'com.appland.appmap.App'
-}
-
repositories {
jcenter()
mavenCentral()
}
dependencies {
- implementation 'com.google.guava:guava:28.0-jre'
- implementation 'info.picocli:picocli:4.0.4'
implementation 'org.yaml:snakeyaml:1.25'
implementation 'com.alibaba:fastjson:1.2.61'
implementation 'org.javassist:javassist:3.25.0-GA'
@@ -36,7 +30,7 @@ dependencies {
jar {
manifest {
- attributes 'Premain-Class': application.mainClassName
+ attributes 'Premain-Class': 'com.appland.appmap.Agent'
}
}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 5c2d1cf0..cc4fdc29 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 7c4388a9..94920145 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 83f2acfd..2fe81a7d 100755
--- a/gradlew
+++ b/gradlew
@@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
else
eval `echo args$i`="\"$arg\""
fi
- i=$((i+1))
+ i=`expr $i + 1`
done
case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@@ -175,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
-APP_ARGS=$(save "$@")
+APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
exec "$JAVACMD" "$@"
diff --git a/src/main/java/com/appland/appmap/Agent.java b/src/main/java/com/appland/appmap/Agent.java
new file mode 100644
index 00000000..3f3149aa
--- /dev/null
+++ b/src/main/java/com/appland/appmap/Agent.java
@@ -0,0 +1,58 @@
+package com.appland.appmap;
+
+import com.appland.appmap.config.AppMapConfig;
+import com.appland.appmap.record.RuntimeRecorder;
+import com.appland.appmap.transform.ClassFileTransformer;
+
+import java.io.File;
+import java.lang.instrument.Instrumentation;
+
+/**
+ * Agent is a JVM agent which instruments, records, and prints appmap files
+ * for a program. To use the AppMap agent, start the progress with the JVM argument
+ * -javaagent:/path/to/appmap-java.jar. The agent will read
+ * the appmap.yml configuration file, which tells it which classes
+ * to instrument. Classes will be instrumented automatically as they are loaded by the
+ * JVM. As instrumented classes are used by the program, the activity is recorded by the agent.
+ * In some cases, such as JUnit, AppMap files will be printed as the program executes.
+ * When the agent exits, any un-printed data will be written to the file appmap.json.
+ */
+public class Agent {
+
+ private final static String DEFAULT_CONFIG_FILE = "appmap.yml";
+
+ /**
+ * premain is the entry point for the AppMap Java agent.
+ * @param agentArgs agent options
+ * @param inst services needed to instrument Java programming language code
+ * @see Package java.lang.instrument
+ */
+ public static void premain(String agentArgs, Instrumentation inst) {
+ System.err.println("AppMap agent is loaded");
+
+ String appmapPath = System.getProperty("appmap.config.file");
+ if (appmapPath == null) {
+ appmapPath = DEFAULT_CONFIG_FILE;
+ }
+
+ System.err.println("Reading appmap configuration from " + appmapPath);
+
+ if (AppMapConfig.load(new File(appmapPath)) == null) {
+ System.err.printf("AppMap: failed to load config %s\n", appmapPath);
+ return;
+ }
+
+ inst.addTransformer(new ClassFileTransformer());
+
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+ public void run() {
+ RuntimeRecorder runtimeRecorder = RuntimeRecorder.get();
+ if (runtimeRecorder.isEmpty()) {
+ return;
+ }
+
+ runtimeRecorder.flushToFile("appmap.json");
+ }
+ }, "Shutdown-thread"));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/appland/appmap/App.java b/src/main/java/com/appland/appmap/App.java
deleted file mode 100644
index e83bb44b..00000000
--- a/src/main/java/com/appland/appmap/App.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.appland.appmap;
-
-import java.lang.instrument.Instrumentation;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.PrintWriter;
-
-import com.appland.appmap.commands.Inspect;
-import com.appland.appmap.commands.Record;
-import com.appland.appmap.commands.Upload;
-import com.appland.appmap.config.AppMapConfig;
-import com.appland.appmap.record.RuntimeRecorder;
-import com.appland.appmap.transform.ClassFileTransformer;
-
-import picocli.CommandLine;
-import picocli.CommandLine.Command;
-
-@Command(name = "appmap",
- mixinStandardHelpOptions = true,
- subcommands = {
- Inspect.class,
- Record.class,
- Upload.class,
-})
-public class App implements Runnable {
-
- private final static String DEFAULT_CONFIG_FILE = "appmap.yml";
-
- @Override public void run() { }
-
- /**
- * This method is the entry point of the CLI.
- */
- public static void main(String[] args) {
- CommandLine cmd = new CommandLine(new App());
- if (args.length == 0) {
- cmd.usage(System.out);
- return;
- }
-
- Integer exitCode = cmd.execute(args);
- System.exit(exitCode);
- }
-
- public static void premain(String agentArgs, Instrumentation inst) {
- System.err.println("AppMap: loaded");
-
- String appmapPath = System.getProperty("appmap.config.file");
- if (appmapPath == null) {
- appmapPath = DEFAULT_CONFIG_FILE;
- }
-
- if (AppMapConfig.load(new File(appmapPath)) == null) {
- System.err.printf("AppMap: failed to load config %s\n", appmapPath);
- return;
- }
-
- inst.addTransformer(new ClassFileTransformer());
-
- Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
- public void run() {
- RuntimeRecorder runtimeRecorder = RuntimeRecorder.get();
- if (runtimeRecorder.isEmpty()) {
- return;
- }
-
- runtimeRecorder.flushToFile("appmap.json");
- }
- }, "Shutdown-thread"));
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/appland/appmap/commands/Inspect.java b/src/main/java/com/appland/appmap/commands/Inspect.java
deleted file mode 100644
index 2d9c5066..00000000
--- a/src/main/java/com/appland/appmap/commands/Inspect.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.appland.appmap.commands;
-
-import com.appland.appmap.config.AppMapConfig;
-import com.appland.appmap.config.AppMapPackage;
-
-// import com.appland.appmap.debugger.Trace;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-
-import java.util.ArrayList;
-import java.util.concurrent.Callable;
-
-import javax.sound.midi.SysexMessage;
-
-import picocli.CommandLine.Command;
-import picocli.CommandLine.Option;
-import picocli.CommandLine.Parameters;
-
-@Command(name = "inspect",
- description = "Inspect code and generate a classmap file")
-public class Inspect implements Callable {
- @Option(names = { "-o", "--output" },
- description = "Name of the output file (default: ${DEFAULT-VALUE})")
- private File filename = new File("appmap.yml");
-
- @Option(names = { "-c", "--class-path" },
- description = "Specifies a list of directories, JAR archives, and ZIP archives to search for class files. Class path entries are separated by colons (:).")
- private String[] classPath;
-
- @Option(names = { "-j", "--jar" },
- description = "Name of the jar file to be invoked")
- private File jarPath;
-
- @Option(names = { "-m", "--main" },
- description = "Name of the main class")
- private String mainClass;
-
- @Option(names = { "-a", "--args" }, description = "launch arguments")
- private String[] launchArgs = new String[]{};
-
- @Override
- public Void call() {
- // AppMapConfig config = AppMapConfig.load(filename);
- // if (config == null) {
- // return null;
- // }
-
- // Trace trace = new Trace();
-
- // ArrayList classPaths = new ArrayList();
- // for (AppMapPackage p : config.packages) {
- // trace.includeClassPath(p.path);
-
- // if (p.exclude == null) {
- // continue;
- // }
-
- // for (String exclusion : p.exclude) {
- // trace.excludeClassPath(exclusion);
- // }
- // }
-
- // mainClass = String.format("%s", mainClass, String.join(" ", launchArgs));
-
- // if (jarPath != null) {
- // if (classPath != null && classPath.length > 0) {
- // System.err.println("warn: both jar and class path options provided.");
- // System.err.println(" only jar will be used.");
- // }
- // trace.execute(mainClass, jarPath);
- // return null;
- // }
-
- // if (mainClass == null || mainClass.isBlank()) {
- // System.err.println("error: a jar path or main class must be provided");
- // return null;
- // }
-
- // mainClass = String.format("%s", mainClass, String.join(" ", launchArgs));
-
- // if (classPath != null && classPath.length > 0) {
- // // if -c is provided many times, join them
- // String joinedClassPath = String.join(":", classPath);
- // trace.execute(mainClass, joinedClassPath);
- // return null;
- // }
-
- // trace.execute(mainClass);
-
- // // System.out.println(trace.serialize());
-
- return null;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/appland/appmap/commands/Record.java b/src/main/java/com/appland/appmap/commands/Record.java
deleted file mode 100644
index 4674ff95..00000000
--- a/src/main/java/com/appland/appmap/commands/Record.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.appland.appmap.commands;
-
-import java.io.File;
-import java.util.List;
-import java.util.concurrent.Callable;
-
-import picocli.CommandLine.Command;
-import picocli.CommandLine.Option;
-import picocli.CommandLine.Parameters;
-
-@Command(name = "record",
- description = "Record the execution of a program and generate an AppMap")
-public class Record implements Callable {
- @Option(names = { "-o", "--output" },
- description = "Name of the output file (default: ${DEFAULT-VALUE})")
- private File filename = new File("appmap.json");
-
- @Parameters(description = "Name of the executable archive to run and record")
- private File executable;
-
- @Override
- public Void call() {
- return null;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/appland/appmap/commands/Upload.java b/src/main/java/com/appland/appmap/commands/Upload.java
deleted file mode 100644
index 1b46560b..00000000
--- a/src/main/java/com/appland/appmap/commands/Upload.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.appland.appmap.commands;
-
-import java.io.File;
-import java.util.List;
-import java.util.concurrent.Callable;
-
-import picocli.CommandLine.Command;
-import picocli.CommandLine.Option;
-import picocli.CommandLine.Parameters;
-
-@Command(name = "upload",
- description = "Upload a scenario file to AppLand")
-public class Upload implements Callable {
- @Parameters(description = "Name of the file to upload")
- private File filename;
-
- @Option(names = { "--open" },
- negatable = true,
- description = "Whether to open the new scenario in the browser (default: ${DEFAULT-VALUE})")
- private Boolean open = true;
-
- @Option(names = { "--owner" },
- description = "User id to own the scenario (default: ${DEFAULT-VALUE})")
- private int owner = 1;
-
- @Option(names = { "--url" },
- description = "AppLand website URL (default: ${DEFAULT-VALUE})")
- private String url = "https://appland-staging.herokuapp.com";
-
- @Override
- public Void call() {
- return null;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/appland/appmap/transform/HookableConfigPath.java b/src/main/java/com/appland/appmap/transform/HookableConfigPath.java
index 749180ee..c0391b66 100644
--- a/src/main/java/com/appland/appmap/transform/HookableConfigPath.java
+++ b/src/main/java/com/appland/appmap/transform/HookableConfigPath.java
@@ -22,14 +22,6 @@ protected Boolean match(CtBehavior behavior) {
}
final String className = behavior.getDeclaringClass().getName();
- // if (className.contains("$")) {
- // return false;
- // }
-
- if (className.equals("com.appland.appmap.App")) {
- System.out.println("xxx");
- }
-
return AppMapConfig.get().includes(className);
}
}
diff --git a/src/main/java/com/appland/appmap/transform/HookableInterfaceName.java b/src/main/java/com/appland/appmap/transform/HookableInterfaceName.java
index b565209e..e5fd1343 100644
--- a/src/main/java/com/appland/appmap/transform/HookableInterfaceName.java
+++ b/src/main/java/com/appland/appmap/transform/HookableInterfaceName.java
@@ -26,7 +26,10 @@ protected Boolean match(CtClass classType) {
}
}
} catch (NotFoundException e) {
- // fall through
+ // TODO: May want to handle this exception in a dedicated method, so we
+ // can be sure where the exception has occurred.
+ //
+ // In general, allow this exception to fall through.
}
return false;
diff --git a/src/test/java/com/appland/appmap/AppTest.java b/src/test/java/com/appland/appmap/AppTest.java
index 2061d91e..da709d0b 100644
--- a/src/test/java/com/appland/appmap/AppTest.java
+++ b/src/test/java/com/appland/appmap/AppTest.java
@@ -4,13 +4,11 @@
package com.appland.appmap;
import org.junit.Test;
-import static org.junit.Assert.*;
-import com.appland.appmap.commands.Inspect;
public class AppTest {
@Test
public void testAppHasAGreeting() {
- Inspect classUnderTest = new Inspect();
- classUnderTest.call();
+ SayHello classUnderTest = new SayHello();
+ classUnderTest.hello();
}
}
diff --git a/src/test/java/com/appland/appmap/SayHello.java b/src/test/java/com/appland/appmap/SayHello.java
new file mode 100644
index 00000000..84dcd9f1
--- /dev/null
+++ b/src/test/java/com/appland/appmap/SayHello.java
@@ -0,0 +1,7 @@
+package com.appland.appmap;
+
+public class SayHello {
+ public String hello() {
+ return "hello";
+ }
+}