Skip to content

Commit 4dd0912

Browse files
committed
Fix #13 When Scala hasn't been loaded, load the latest available version, instead of the hard coded 2.11.4
1 parent c8edde5 commit 4dd0912

File tree

7 files changed

+111
-107
lines changed

7 files changed

+111
-107
lines changed

README.md

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,46 @@ JVM processes without any prior setup at the target process.
66
## Download
77

88
Download and extract
9-
[scalive-1.5.zip](https://github.com/xitrum-framework/scalive/releases/download/1.5/scalive-1.5.zip),
9+
[scalive-1.6.zip](https://github.com/xitrum-framework/scalive/releases/download/1.6/scalive-1.6.zip),
1010
you will see:
1111

1212
```
13-
scalive-1.5/
13+
scalive-1.6/
1414
scalive
1515
scalive.cmd
16-
scalive-1.5.jar
16+
scalive-1.6.jar
1717
18-
scala-library-2.10.4.jar
19-
scala-compiler-2.10.4.jar
20-
scala-reflect-2.10.4.jar
18+
scala-library-2.10.6.jar
19+
scala-compiler-2.10.6.jar
20+
scala-reflect-2.10.6.jar
2121
22-
scala-library-2.11.4.jar
23-
scala-compiler-2.11.4.jar
24-
scala-reflect-2.11.4.jar
22+
scala-library-2.11.8.jar
23+
scala-compiler-2.11.8.jar
24+
scala-reflect-2.11.8.jar
2525
```
2626

27-
scala-library, scala-compiler, and scala-reflect of the appropriate version
28-
will be loaded to your running JVM process, if they have not been loaded.
29-
The REPL console needs these libraries.
27+
scala-library, scala-compiler, and scala-reflect of the correct version
28+
that your JVM process is using will be loaded, if they have not been loaded.
29+
The REPL console needs these libraries to work.
3030

31-
For example, your process has already loaded scala-library 2.10.4 by itself,
31+
For example, your process has already loaded scala-library 2.11.8 by itself,
3232
but scala-compiler and scala-reflect haven't been loaded, Scalive will
33-
automatically load their version 2.10.4.
33+
automatically load their version 2.11.8.
3434

35-
If none of them has been loaded, Scalive will load version 2.11.4.
35+
If none of them has been loaded, i.e. your process doesn't use Scala,
36+
Scalive will load the lastest version in the directory.
3637

37-
For convenience, Scala 2.10.4 and 2.11.4 JARs are preincluded. If your
38-
process is using a different Scala version, you need to manually download the
39-
corresponding JARs from the Internet and save them in the same directory as
40-
above.
38+
For your convenience, Scala 2.10.6 and 2.11.8 JARs are included above.
39+
40+
If your process is using a different Scala version, you need to manually
41+
download the corresponding JARs from the Internet and save them in the
42+
same directory as above.
4143

4244
## Usage
4345

4446
Run the shell script `scalive` (*nix) or `scalive.cmd` (Windows).
4547

46-
To see a list of running JVM processes and their process IDs:
48+
Run without argument to see the list of running JVM process IDs on your local machine:
4749

4850
```
4951
scalive
@@ -52,25 +54,26 @@ scalive
5254
To connect a Scala REPL console to a process:
5355

5456
```
55-
scalive <pid>
57+
scalive <process id listed above>
5658
```
5759

58-
## How to add your own JARs
60+
## How to load your own JARs to the process
61+
62+
Scalive only automatically loads `scala-library.jar`, `scala-compiler.jar`,
63+
`scala-reflect.jar`, and `scalive.jar` to the system classpath.
5964

60-
Scalive only automatically adds `scala-library.jar`, `scala-compiler.jar`,
61-
`scala-reflect.jar`, and `scalive.jar` to the system classpath. If you want to
62-
load additional classes in other JARs, first run these in the REPL console to
63-
add the JAR to the system class loader:
65+
If you want to load additional classes in other JARs, first run these in the
66+
REPL console to load the JAR to the system class loader:
6467

6568
```
66-
val cl = ClassLoader.getSystemClassLoader.asInstanceOf[java.net.URLClassLoader]
67-
val searchDirs = Array("/dir/containing/the/jar")
68-
val jarbase = "mylib" // Will match "mylibxxx.jar", convenient when there's version number in the file name
69-
scalive.Classpath.findAndAddJar(cl, searchDirs, jarbase)
69+
val cl = ClassLoader.getSystemClassLoader.asInstanceOf[java.net.URLClassLoader]
70+
val jarSearchDirs = Array("/dir/containing/the/jar")
71+
val jarPrefix = "mylib" // Will match "mylib-xxx.jar", convenient when there's version number in the file name
72+
scalive.Classpath.findAndAddJar(cl, jarSearchDirs, jarPrefix)
7073
```
7174

7275
Now the trick is just quit the REPL console and connect it to the target process
73-
again. You will be able to use your classes in the JAR as normal:
76+
again. You will be able to use your classes in the JAR normally:
7477

7578
```
7679
import mylib.foo.Bar
@@ -81,7 +84,7 @@ import mylib.foo.Bar
8184

8285
## How Scalive works
8386

84-
Scalive uses the [Attach API](https://blogs.oracle.com/CoreJavaTechTips/entry/the_attach_api)
87+
Scalive uses the [Attach API](https://blogs.oracle.com/CoreJavaTechTips/entry/the_attach_api) in Java 6
8588
to tell the target process to load an [agent](http://javahowto.blogspot.jp/2006/07/javaagent-option.html).
8689

8790
The agent then creates a TCP server to let the Scalive process interact with the
@@ -96,13 +99,12 @@ console for Clojure.
9699

97100
For simplicity and to avoid memory leak when you attach/detach many times,
98101
Scalive only supports processes with only the default system class loader,
99-
without additional class loaders (Ex: normal standalone JVM processes, like
102+
without additional class loaders. Usually they are standalone JVM processes,
103+
like
100104
[Play](http://www.playframework.com/) or
101-
[Xitrum](http://xitrum-framework.github.io/) in production mode).
105+
[Xitrum](http://xitrum-framework.github.io/) in production mode.
102106

103-
Processes with multiple class loaders like Tomcat or
104-
[SBT](http://www.scala-sbt.org/) are not supported (with SBT, you already has
105-
the SBT console, so it's not a big deal).
107+
Processes with multiple class loaders like Tomcat are currently not supported.
106108

107109
2.
108110

build.sbt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
organization := "tv.cntt"
22
name := "scalive"
3-
version := "1.5-SNAPSHOT"
3+
version := "1.6"
44

55
scalaVersion := "2.11.6"
6-
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.11.6" % "provided"
6+
libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % "provided"
77
autoScalaLibrary := false
88
crossPaths := false // Do not append Scala versions to the generated artifacts
99

1010
javacOptions ++= Seq("-Xlint:deprecation")
1111

1212
// Ensure Scalive can run on Java 6
1313
scalacOptions += "-target:jvm-1.6"
14-
15-
// Ensure Scalive can run on Java 6
1614
javacOptions ++= Seq("-source", "1.6", "-target", "1.6")
1715

1816
// Add tools.jar to classpath

dev/README.rst

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,21 @@ This is the directory that will be zipped when Scalive is released.
2525
zip/
2626
scalive
2727
scalive.cmd
28-
scalive-<version>-SNAPSHOT.jar -> ../../target/scala-2.11/scalive-<version>-SNAPSHOT.jar
28+
scalive-<version>.jar -> ../../target/scala-2.11/scalive-<version>.jar
2929

30-
scala-library-2.10.4.jar
31-
scala-compiler-2.10.4.jar
32-
scala-reflect-2.10.4.jar
30+
scala-library-2.10.6.jar
31+
scala-compiler-2.10..jar
32+
scala-reflect-2.10.6.jar
3333

34-
scala-library-2.11.4.jar
35-
scala-compiler-2.11.4.jar
36-
scala-reflect-2.11.4.jar
34+
scala-library-2.11.8.jar
35+
scala-compiler-2.11.8.jar
36+
scala-reflect-2.11.8.jar
3737

3838
While developing:
3939

40-
* Run ``sbt package`` to create/update scalive.jar.
40+
* Run ``sbt package`` to create/update scalive-<version>.jar.
4141
* Add missing JARs as above.
42-
* Run ``scalive`` to attach to a certain JVM process to see if it works properly.
42+
* Run ``scalive`` to attach to a JVM process to see if it works properly.
4343

4444
Release
4545
-------

src/main/java/scalive/Agent.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,23 @@
77

88
public class Agent {
99
/**
10-
* @param agentArgs <jarpath1>[<File.pathSeparator><jarpath2>...] <server port>
10+
* @param agentArgs <jarSearchDir1>[<File.pathSeparator><jarSearchDir2>...] <server port>
1111
*
12-
* jarpath is the absolute path this directory:
13-
*
14-
* {{{
15-
* jarpath/
12+
* <pre>{@code
13+
* jarSearchDir/
1614
* scalive.jar
1715
*
1816
* scala-library-2.11.0.jar
1917
* scala-compiler-2.11.0.jar
2018
* scala-reflect-2.11.0.jar
2119
*
2220
* [Other Scala versions]
23-
* }}}
21+
* }</pre>
2422
*/
2523
public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
26-
final String[] args = agentArgs.split(" ");
27-
final String[] jarpaths = args[0].split(File.pathSeparator);
28-
final int port = Integer.parseInt(args[1]);
24+
final String[] args = agentArgs.split(" ");
25+
final String[] jarSearchDirs = args[0].split(File.pathSeparator);
26+
final int port = Integer.parseInt(args[1]);
2927

3028
System.out.println("[Scalive] REPL server starts at port " + port);
3129
final ServerSocket server = new ServerSocket(port);
@@ -42,7 +40,7 @@ public void run() {
4240
try {
4341
Socket client = server.accept(); // Block until a connection comes in
4442
server.close(); // Accept no other clients
45-
Server.serve(client, jarpaths);
43+
Server.serve(client, jarSearchDirs);
4644
} catch (Exception e) {
4745
e.printStackTrace();
4846
}

src/main/java/scalive/AgentLoader.java

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,22 @@
1010

1111
public class AgentLoader {
1212
/**
13-
* @param args <jarpath1>[<File.pathSeparator><jarpath2>...] [pid]
13+
* @param args <jarSearchDir1>[<File.pathSeparator><jarSearchDir2>...] [pid]
1414
*
15-
* jarpath is the absolute path this directory:
16-
*
17-
* {{{
18-
* jarpath/
15+
* <pre>{@code
16+
* jarSearchDir/
1917
* scalive.jar
2018
*
2119
* scala-library-2.11.0.jar
2220
* scala-compiler-2.11.0.jar
2321
* scala-reflect-2.11.0.jar
2422
*
2523
* [Other Scala versions]
26-
* }}}
24+
* }</pre>
2725
*/
2826
public static void main(String[] args) throws Exception {
2927
if (args.length != 1 && args.length != 2) {
30-
System.out.println("Arguments: <jarpath1>[<File.pathSeparator><jarpath2>...] [pid]");
28+
System.out.println("Arguments: <jarSearchDir1>[<File.pathSeparator><jarSearchDir2>...] [pid]");
3129
return;
3230
}
3331

@@ -38,16 +36,16 @@ public static void main(String[] args) throws Exception {
3836
return;
3937
}
4038

41-
String jarpaths = args[0];
39+
String jarSearchDirs = args[0];
4240
String pid = args[1];
43-
loadAgent(jarpaths, pid);
41+
loadAgent(jarSearchDirs, pid);
4442
}
4543

4644
/**
4745
* com.sun.tools.attach.VirtualMachine is in tools.jar, which is not in
4846
* classpath by default:
4947
*
50-
* {{{
48+
* <pre>{@code
5149
* jdk/
5250
* bin/
5351
* java
@@ -56,7 +54,7 @@ public static void main(String[] args) throws Exception {
5654
* java
5755
* lib/
5856
* tools.jar
59-
* }}}
57+
* }</pre>
6058
*/
6159
private static void addToolsDotJarToClasspath() throws Exception {
6260
String path = System.getProperty("java.home") + "/../lib/tools.jar";
@@ -74,13 +72,13 @@ private static void listJvmProcesses() {
7472
}
7573
}
7674

77-
private static void loadAgent(String jarpaths, String pid) throws Exception {
78-
final String[] ary = jarpaths.split(File.pathSeparator);
75+
private static void loadAgent(String jarSearchDirs, String pid) throws Exception {
76+
final String[] ary = jarSearchDirs.split(File.pathSeparator);
7977
final String agentJar = Classpath.findJar(ary, "scalive");
8078
final VirtualMachine vm = VirtualMachine.attach(pid);
8179
final int port = Client.getFreePort();
8280

83-
vm.loadAgent(agentJar, jarpaths + " " + port);
81+
vm.loadAgent(agentJar, jarSearchDirs + " " + port);
8482
Runtime.getRuntime().addShutdownHook(new Thread() {
8583
@Override public void run() {
8684
try {

src/main/java/scalive/Classpath.java

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,35 @@ private static Method getAddURL() {
2424
//--------------------------------------------------------------------------
2525

2626
/**
27-
* @param jarpaths Directories to search for the JAR
27+
* Finds the ".jar" file with the {@code jarPrefix} and the latest version.
2828
*
29-
* @param jarbase JAR file name prefix; Ex: "scalive-agent" will match "scalive-agent-xxx.jar"
29+
* @param jarSearchDirs Directories to search for the JAR
30+
*
31+
* @param jarPrefix JAR file name prefix to search; example: "scalive-agent" will match "scalive-agent-xxx.jar"
3032
*/
31-
public static String findJar(String[] jarpaths, String jarbase) throws Exception {
32-
for (String jarpath: jarpaths) {
33-
File dir = new File(jarpath);
33+
public static String findJar(String[] jarSearchDirs, String jarPrefix) throws Exception {
34+
String maxBaseName = null;
35+
File maxFile = null;
36+
for (String jarSearchDir: jarSearchDirs) {
37+
File dir = new File(jarSearchDir);
3438
File[] files = dir.listFiles();
39+
if (files == null) continue;
3540

36-
for (int i = 0; i < files.length; i++) {
37-
File file = files[i];
38-
String name = file.getName();
39-
if (file.isFile() && name.endsWith(".jar") && name.startsWith(jarbase))
40-
return file.getPath();
41+
for (File file : files) {
42+
String baseName = file.getName();
43+
if (file.isFile() && baseName.endsWith(".jar") && baseName.startsWith(jarPrefix)) {
44+
if (maxBaseName == null || baseName.compareTo(maxBaseName) > 0) {
45+
maxBaseName = baseName;
46+
maxFile = file;
47+
}
48+
}
4149
}
4250
}
4351

44-
throw new Exception("Could not find " + jarbase + " in " + join(jarpaths, File.pathSeparator));
52+
if (maxFile == null)
53+
throw new Exception("Could not find " + jarPrefix + " in " + join(jarSearchDirs, File.pathSeparator));
54+
else
55+
return maxFile.getPath();
4556
}
4657

4758
public static void addPath(URLClassLoader cl, String path) throws Exception {
@@ -50,24 +61,24 @@ public static void addPath(URLClassLoader cl, String path) throws Exception {
5061
if (!Arrays.asList(urls).contains(url)) addURL.invoke(cl, url);
5162
}
5263

53-
/** Combination of findJar and addPath. */
54-
public static void findAndAddJar(URLClassLoader cl, String[] jarpaths, String jarbase) throws Exception {
55-
String jar = findJar(jarpaths, jarbase);
64+
/** Combination of {@link #findJar(String[], String)} and {@link #addPath(URLClassLoader, String)}. */
65+
public static void findAndAddJar(URLClassLoader cl, String[] jarSearchDirs, String jarPrefix) throws Exception {
66+
String jar = findJar(jarSearchDirs, jarPrefix);
5667
addPath(cl, jar);
68+
System.out.println("[Scalive] Load " + jar);
5769
}
5870

5971
/**
60-
* Similar to findAndAddJar without representativeClass, but only find and
61-
* add the JAR to classpath if the representativeClass has not been loaded.
72+
* Similar to {@link #findAndAddJar(URLClassLoader, String[], String)} without {@code representativeClass},
73+
* but only find and add the JAR to classpath if the representativeClass has not been loaded.
6274
*/
6375
public static void findAndAddJar(
64-
URLClassLoader cl, String[] jarpaths, String jarbase, String representativeClass
76+
URLClassLoader cl, String representativeClass, String[] jarSearchDirs, String jarPrefix
6577
) throws Exception {
6678
try {
6779
Class.forName(representativeClass, true, cl);
6880
} catch (ClassNotFoundException e) {
69-
System.out.println("[Scalive] Load " + jarbase);
70-
findAndAddJar(cl, jarpaths, jarbase);
81+
findAndAddJar(cl, jarSearchDirs, jarPrefix);
7182
}
7283
}
7384

@@ -86,11 +97,11 @@ public static String getScalaVersion(ClassLoader cl) throws Exception {
8697
//--------------------------------------------------------------------------
8798

8899
private static String join(Object[] xs, String separator) {
89-
StringBuffer buf = new StringBuffer();
100+
StringBuilder b = new StringBuilder();
90101
for (Object x: xs) {
91-
if (buf.length() > 0) buf.append(separator);
92-
buf.append(x);
102+
if (b.length() > 0) b.append(separator);
103+
b.append(x);
93104
}
94-
return buf.toString();
105+
return b.toString();
95106
}
96107
}

0 commit comments

Comments
 (0)