Permalink
Browse files

Initial checkin

  • Loading branch information...
0 parents commit 05aa391119fb4edc0f966d3571dfc9ef4b5f7b69 @djpowell committed Oct 18, 2009
@@ -0,0 +1,6 @@
+**/.classpath
+**/.project
+**/.settings
+**/*.jardesc
+**/.settings/**
+liverepl-server/lib/clojure.jar
@@ -0,0 +1,40 @@
+Clojure Live REPL - 2009-10-18
+David Powell <djpowell@djpowell.net>
+<http://github.com/djpowell/liverepl>
+
+----------------------------------------
+
+Build
+=====
+
+To build:
+
+ Copy clojure.jar to: ./liverepl-server/lib/clojure.jar
+
+ Run ant
+
+The build will be copied to: ./build/
+
+----------------------------------------
+
+Configuration
+=============
+
+Edit liverepl.bat to point to your installed JDK
+
+----------------------------------------
+
+Opearation
+==========
+
+To see a list of running Java processes on the system, and their
+process ids, enter:
+
+ liverepl
+
+To connect a repl shell to a running Java process, enter:
+
+ liverepl <pid>
+
+ -- where the pid is the process id for the process.
+
@@ -0,0 +1,24 @@
+<project name="liverepl" basedir="." default="build">
+
+ <target name="clean" description="delete build and classes">
+ <ant dir="./liverepl-agent" target="clean" />
+ <ant dir="./liverepl-server" target="clean" />
+ <delete dir="./build" />
+ </target>
+
+ <target name="build" description="build">
+ <mkdir dir="./build" />
+ <ant dir="./liverepl-agent" />
+ <ant dir="./liverepl-server" />
+ <copy todir="./build">
+ <fileset dir="./liverepl-server/build" />
+ <fileset dir="./liverepl-server/lib" />
+ <fileset dir="./liverepl-agent/build" />
+ <fileset dir="./liverepl-agent/lib" />
+ <fileset file="./liverepl.bat" />
+ <fileset file="./README.TXT" />
+ </copy>
+ </target>
+
+</project>
+
@@ -0,0 +1,29 @@
+<project name="liverepl-agent" basedir="." default="jar">
+
+ <target name="clean" description="delete build and classes">
+ <delete dir="./build" />
+ <delete dir="./classes" />
+ </target>
+
+ <target name="compile">
+ <mkdir dir="./classes" />
+ <javac debug="yes"
+ srcdir="./src"
+ destdir="./classes"
+ includes="**/*.java">
+ <classpath>
+ <fileset dir="./lib" includes="*.jar" />
+ </classpath>
+ </javac>
+ </target>
+
+ <target name="jar" description="build liverepl-agent.jar" depends="compile">
+ <mkdir dir="./build" />
+ <jar destfile="build/liverepl-agent.jar" manifest="src/META-INF/MANIFEST.MF">
+ <fileset dir="./classes" />
+ <fileset dir="./src" includes="**/*.clj" />
+ </jar>
+ </target>
+
+</project>
+
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Agent-Class: net.djpowell.liverepl.agent.Agent
+Main-Class: net.djpowell.liverepl.client.Main
@@ -0,0 +1,103 @@
+package net.djpowell.liverepl.agent;
+
+import java.io.File;
+import java.lang.instrument.Instrumentation;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.logging.Logger;
+
+import net.djpowell.liverepl.client.Main;
+
+public class Agent {
+
+ private static ClassLoader pushClassLoader(List<URL> urls) {
+ TRC.fine("Creating new classloader with: " + urls);
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
+ TRC.fine("Old classloader: " + old);
+ URLClassLoader withClojure = new URLClassLoader(urls.toArray(new URL[urls.size()]), old); // TODO
+ Thread.currentThread().setContextClassLoader(withClojure);
+ return old;
+ }
+
+ private static void popClassLoader(ClassLoader old) {
+ TRC.fine("Restoring old context classloader");
+ Thread.currentThread().setContextClassLoader(old);
+ }
+
+ private static boolean isClojureLoaded() {
+ try {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ cl.loadClass("clojure.lang.RT");
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+
+ public static void agentmain(String agentArgs, Instrumentation inst) {
+ TRC.fine("Started Attach agent");
+
+ StringTokenizer stok = new StringTokenizer(agentArgs, "\n");
+ if (stok.countTokens() != 3) {
+ throw new RuntimeException("Invalid parameters: " + agentArgs);
+ }
+
+ int port = Integer.parseInt(stok.nextToken());
+ TRC.fine("Port: " + port);
+ String clojurePath = stok.nextToken();
+ String serverPath = stok.nextToken();
+
+ boolean clojureLoaded = isClojureLoaded();
+ TRC.fine("Clojure is " + (clojureLoaded ? "" : "not ") + "loaded");
+
+ List<URL> urls;
+ if (clojureLoaded) {
+ urls = getJarUrls(serverPath);
+ } else {
+ urls = getJarUrls(clojurePath, serverPath);
+ }
+
+ ClassLoader old = pushClassLoader(urls);
+ try {
+ if (!clojureLoaded) { // if clojure wasn't loaded before, print current status
+ TRC.fine("Clojure is " + (isClojureLoaded() ? "" : "not ") + "loaded");
+ }
+ startRepl(port);
+ } finally {
+ popClassLoader(old);
+ }
+ }
+
+ private static List<URL> getJarUrls(String... paths) {
+ List<URL> urls = new ArrayList<URL>();
+ try {
+ for (String path : paths) {
+ URL url = new File(path).toURI().toURL();
+ urls.add(url);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return urls;
+ }
+
+ private static void startRepl(int port) {
+ // avoids making load-time references to Clojure classes from the system classloader
+ try {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ Class<?> repl = Class.forName("net.djpowell.liverepl.server.Repl", true, cl);
+ Method method = repl.getMethod("main", InetAddress.class, Integer.TYPE);
+ method.invoke(null, Main.LOCALHOST, port);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static final Logger TRC = Logger.getLogger(Agent.class.getName());
+
+}
@@ -0,0 +1,70 @@
+package net.djpowell.liverepl.client;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.logging.Logger;
+
+public class Console {
+
+ private static String NEWLINE = System.getProperty("line.separator");
+
+ public static void main(InetAddress host, int port) throws Exception {
+ final BufferedReader cin = new BufferedReader(new InputStreamReader(System.in));
+ final Writer cout = new OutputStreamWriter(System.out);
+ Socket s = new Socket(host, port);
+ try {
+ final Reader sin = new InputStreamReader(s.getInputStream());
+ final Writer sout = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
+
+ Thread ch = new Thread("ConsoleHandler") {
+ @Override
+ public void run() {
+ try {
+ for (;;) {
+ // use line-based i/o for reading from the keyboard
+ String line = cin.readLine();
+ sout.write(line + NEWLINE);
+ sout.flush();
+ }
+ } catch (IOException e) {
+ TRC.fine(e.getMessage());
+ }
+ }
+ };
+ ch.start();
+
+ Thread sh = new Thread("SocketHandler") {
+ @Override
+ public void run() {
+ try {
+ for (;;) {
+ // use character based i/o for printing server responses
+ char c = (char) sin.read();
+ cout.write(c);
+ cout.flush();
+ }
+ } catch (IOException e) {
+ TRC.fine(e.getMessage());
+ }
+ }
+ };
+ sh.start();
+
+ // block until both threads finish
+ sh.join();
+ ch.join();
+ } finally {
+ s.close();
+ }
+ }
+
+ private static final Logger TRC = Logger.getLogger(Console.class.getName());
+
+}
@@ -0,0 +1,77 @@
+package net.djpowell.liverepl.client;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.UnknownHostException;
+import java.util.logging.Logger;
+
+import com.sun.tools.attach.VirtualMachine;
+import com.sun.tools.attach.VirtualMachineDescriptor;
+
+public class Main {
+
+ // bind to localhost to keep things more secure
+ public static final InetAddress LOCALHOST;
+ static {
+ try {
+ LOCALHOST = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static int getFreePort(InetAddress host) {
+ // open a server socket on a random port, then close it
+ // so that we can tell the server to listen on that port
+ // and we will know which port to connect to
+ try {
+ ServerSocket server = new ServerSocket(0, 0, LOCALHOST);
+ int port = server.getLocalPort();
+ server.close();
+ return port;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void listPids() {
+ System.out.println();
+ System.out.println("liverepl");
+ System.out.println("Usage: liverepl <pid>");
+ System.out.println();
+ System.out.println("List of Java processes");
+ System.out.format("%1$-6s %2$.60s%n", "pid", "Details");
+ for (VirtualMachineDescriptor vmd : VirtualMachine.list()) {
+ System.out.format("%1$-6s %2$.60s%n", vmd.id(), vmd.displayName());
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ // Usage: <clojurepath> <agentjarpath> <serverjarpath> <jvmpid>
+ if (args.length != 4) {
+ listPids();
+ System.exit(0);
+ }
+ String clojurepath = args[0];
+ String agentpath = args[1];
+ String serverpath = args[2];
+ String pid = args[3];
+ TRC.fine("Attaching to pid: " + pid);
+ VirtualMachine vm = VirtualMachine.attach(pid);
+
+ int port = getFreePort(LOCALHOST);
+ // start the agent, which will create the server socket, then return
+ String agentArgs = String.valueOf(port) + "\n" + clojurepath + "\n" + serverpath;
+ vm.loadAgent(agentpath, agentArgs);
+
+ // start the code that will connect to the server socket
+ Console.main(LOCALHOST, port);
+
+ // the server will shutdown when the client disconnects
+ vm.detach();
+ }
+
+ private static final Logger TRC = Logger.getLogger(Main.class.getName());
+
+}
@@ -0,0 +1,33 @@
+<project name="liverepl-server" basedir="." default="jar">
+
+ <target name="clean" description="delete build and classes">
+ <delete dir="./build" />
+ <delete dir="./classes" />
+ </target>
+
+ <target name="compile">
+
+ <echo message="============================================================" />
+ <echo message="Ensure that clojure.jar is copied to ./liverepl-serer/lib" />
+ <echo message="============================================================" />
+
+ <mkdir dir="./classes" />
+ <javac debug="yes"
+ srcdir="./src"
+ destdir="./classes"
+ includes="**/*.java">
+ <classpath>
+ <fileset dir="./lib" includes="*.jar" />
+ </classpath>
+ </javac>
+ </target>
+
+ <target name="jar" description="build liverepl-server.jar" depends="compile">
+ <mkdir dir="./build" />
+ <jar destfile="build/liverepl-server.jar" manifest="src/META-INF/MANIFEST.MF">
+ <fileset dir="./classes" />
+ <fileset dir="./src" includes="**/*.clj" />
+ </jar>
+ </target>
+
+</project>
@@ -0,0 +1 @@
+Manifest-Version: 1.0
Oops, something went wrong.

0 comments on commit 05aa391

Please sign in to comment.