engineyard-visualvm 0.5.3-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/ChangeLog +22 -0
- data/Gemfile +26 -0
- data/LICENSE.txt +23 -0
- data/README.md +104 -0
- data/Rakefile +124 -0
- data/Vagrantfile +94 -0
- data/bin/ey-visualvm +9 -0
- data/cookbooks/apt/README.md +122 -0
- data/cookbooks/apt/files/default/apt-cacher +9 -0
- data/cookbooks/apt/files/default/apt-cacher.conf +144 -0
- data/cookbooks/apt/files/default/apt-proxy-v2.conf +50 -0
- data/cookbooks/apt/metadata.json +34 -0
- data/cookbooks/apt/metadata.rb +13 -0
- data/cookbooks/apt/providers/repository.rb +73 -0
- data/cookbooks/apt/recipes/cacher-client.rb +44 -0
- data/cookbooks/apt/recipes/cacher.rb +45 -0
- data/cookbooks/apt/recipes/default.rb +50 -0
- data/cookbooks/apt/resources/repository.rb +30 -0
- data/cookbooks/gems/recipes/default.rb +16 -0
- data/cookbooks/java/README.md +102 -0
- data/cookbooks/java/attributes/default.rb +29 -0
- data/cookbooks/java/files/default/java.seed +11 -0
- data/cookbooks/java/metadata.json +50 -0
- data/cookbooks/java/metadata.rb +16 -0
- data/cookbooks/java/recipes/default.rb +21 -0
- data/cookbooks/java/recipes/openjdk.rb +39 -0
- data/cookbooks/java/recipes/sun.rb +93 -0
- data/cookbooks/jruby/attributes/default.rb +2 -0
- data/cookbooks/jruby/recipes/default.rb +24 -0
- data/cookbooks/server/recipes/default.rb +9 -0
- data/cookbooks/server/templates/default/server.sh.erb +27 -0
- data/cookbooks/vagrant_main/recipes/default.rb +4 -0
- data/engineyard-visualvm-java.gemspec +47 -0
- data/engineyard-visualvm.gemspec +43 -0
- data/engineyard-visualvm.gemspec.in +35 -0
- data/ext/org/jruby/ext/jmx/Agent.java +169 -0
- data/ext/org/jruby/ext/jmx/JavaHome.java +7 -0
- data/ext/org/jruby/ext/jmx/RMIServerSocketFactoryImpl.java +37 -0
- data/lib/engineyard-visualvm.rb +8 -0
- data/lib/engineyard-visualvm/agent.jar +0 -0
- data/lib/engineyard-visualvm/cli.rb +225 -0
- data/lib/engineyard-visualvm/version.rb +11 -0
- data/spec/engineyard-visualvm_spec.rb +202 -0
- data/spec/spec_helper.rb +49 -0
- metadata +157 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
export PATH=/usr/local/bin:$PATH
|
4
|
+
export JAVA_OPTS=$(jruby -S ey-visualvm jvmargs)
|
5
|
+
|
6
|
+
start() {
|
7
|
+
/sbin/start-stop-daemon --start --verbose --background --make-pidfile \
|
8
|
+
--pidfile server.pid --exec /usr/local/bin/jruby --chdir $PWD \
|
9
|
+
-- -e sleep
|
10
|
+
}
|
11
|
+
|
12
|
+
stop() {
|
13
|
+
/sbin/start-stop-daemon --stop --quiet --pidfile server.pid
|
14
|
+
}
|
15
|
+
|
16
|
+
case $1 in
|
17
|
+
start)
|
18
|
+
start
|
19
|
+
;;
|
20
|
+
stop)
|
21
|
+
stop
|
22
|
+
;;
|
23
|
+
*)
|
24
|
+
start
|
25
|
+
;;
|
26
|
+
esac
|
27
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{engineyard-visualvm}
|
5
|
+
s.version = "0.5.3"
|
6
|
+
s.platform = %q{java}
|
7
|
+
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
9
|
+
s.authors = [%q{Nick Sieger}]
|
10
|
+
s.date = %q{2011-12-21}
|
11
|
+
s.description = %q{This provides a Java agent and command-line utility to enable
|
12
|
+
JMX in any Java process such that it can be accessed through a firewall,
|
13
|
+
and a VisualVM launcher aid to connect to that process through ssh.}
|
14
|
+
s.email = [%q{nick@nicksieger.com}]
|
15
|
+
s.executables = [%q{ey-visualvm}]
|
16
|
+
s.files = [%q{.gitignore}, %q{ChangeLog}, %q{Gemfile}, %q{LICENSE.txt}, %q{README.md}, %q{Rakefile}, %q{Vagrantfile}, %q{bin/ey-visualvm}, %q{cookbooks/apt/README.md}, %q{cookbooks/apt/files/default/apt-cacher}, %q{cookbooks/apt/files/default/apt-cacher.conf}, %q{cookbooks/apt/files/default/apt-proxy-v2.conf}, %q{cookbooks/apt/metadata.json}, %q{cookbooks/apt/metadata.rb}, %q{cookbooks/apt/providers/repository.rb}, %q{cookbooks/apt/recipes/cacher-client.rb}, %q{cookbooks/apt/recipes/cacher.rb}, %q{cookbooks/apt/recipes/default.rb}, %q{cookbooks/apt/resources/repository.rb}, %q{cookbooks/gems/recipes/default.rb}, %q{cookbooks/java/README.md}, %q{cookbooks/java/attributes/default.rb}, %q{cookbooks/java/files/default/java.seed}, %q{cookbooks/java/metadata.json}, %q{cookbooks/java/metadata.rb}, %q{cookbooks/java/recipes/default.rb}, %q{cookbooks/java/recipes/openjdk.rb}, %q{cookbooks/java/recipes/sun.rb}, %q{cookbooks/jruby/attributes/default.rb}, %q{cookbooks/jruby/recipes/default.rb}, %q{cookbooks/server/recipes/default.rb}, %q{cookbooks/server/templates/default/server.sh.erb}, %q{cookbooks/vagrant_main/recipes/default.rb}, %q{engineyard-visualvm-java.gemspec}, %q{engineyard-visualvm.gemspec}, %q{engineyard-visualvm.gemspec.in}, %q{ext/org/jruby/ext/jmx/Agent.java}, %q{ext/org/jruby/ext/jmx/JavaHome.java}, %q{ext/org/jruby/ext/jmx/RMIServerSocketFactoryImpl.java}, %q{lib/engineyard-visualvm.rb}, %q{lib/engineyard-visualvm/agent.jar}, %q{lib/engineyard-visualvm/cli.rb}, %q{lib/engineyard-visualvm/version.rb}, %q{spec/engineyard-visualvm_spec.rb}, %q{spec/spec_helper.rb}]
|
17
|
+
s.homepage = %q{https://github.com/engineyard/engineyard-visualvm}
|
18
|
+
s.require_paths = [%q{lib}]
|
19
|
+
s.rubyforge_project = %q{jruby-extras}
|
20
|
+
s.rubygems_version = %q{1.8.9}
|
21
|
+
s.summary = %q{Client and server helpers for using JMX and VisualVM with EY Cloud.}
|
22
|
+
s.test_files = [%q{spec/engineyard-visualvm_spec.rb}, %q{spec/spec_helper.rb}]
|
23
|
+
|
24
|
+
if s.respond_to? :specification_version then
|
25
|
+
s.specification_version = 3
|
26
|
+
|
27
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
28
|
+
s.add_runtime_dependency(%q<childprocess>, [">= 0"])
|
29
|
+
s.add_runtime_dependency(%q<engineyard>, [">= 0"])
|
30
|
+
s.add_runtime_dependency(%q<jruby-openssl>, [">= 0"])
|
31
|
+
s.add_runtime_dependency(%q<ffi-ncurses>, [">= 0"])
|
32
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
33
|
+
else
|
34
|
+
s.add_dependency(%q<childprocess>, [">= 0"])
|
35
|
+
s.add_dependency(%q<engineyard>, [">= 0"])
|
36
|
+
s.add_dependency(%q<jruby-openssl>, [">= 0"])
|
37
|
+
s.add_dependency(%q<ffi-ncurses>, [">= 0"])
|
38
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
39
|
+
end
|
40
|
+
else
|
41
|
+
s.add_dependency(%q<childprocess>, [">= 0"])
|
42
|
+
s.add_dependency(%q<engineyard>, [">= 0"])
|
43
|
+
s.add_dependency(%q<jruby-openssl>, [">= 0"])
|
44
|
+
s.add_dependency(%q<ffi-ncurses>, [">= 0"])
|
45
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{engineyard-visualvm}
|
5
|
+
s.version = "0.5.3"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = [%q{Nick Sieger}]
|
9
|
+
s.date = %q{2011-12-21}
|
10
|
+
s.description = %q{This provides a Java agent and command-line utility to enable
|
11
|
+
JMX in any Java process such that it can be accessed through a firewall,
|
12
|
+
and a VisualVM launcher aid to connect to that process through ssh.}
|
13
|
+
s.email = [%q{nick@nicksieger.com}]
|
14
|
+
s.executables = [%q{ey-visualvm}]
|
15
|
+
s.files = [%q{.gitignore}, %q{ChangeLog}, %q{Gemfile}, %q{LICENSE.txt}, %q{README.md}, %q{Rakefile}, %q{Vagrantfile}, %q{bin/ey-visualvm}, %q{cookbooks/apt/README.md}, %q{cookbooks/apt/files/default/apt-cacher}, %q{cookbooks/apt/files/default/apt-cacher.conf}, %q{cookbooks/apt/files/default/apt-proxy-v2.conf}, %q{cookbooks/apt/metadata.json}, %q{cookbooks/apt/metadata.rb}, %q{cookbooks/apt/providers/repository.rb}, %q{cookbooks/apt/recipes/cacher-client.rb}, %q{cookbooks/apt/recipes/cacher.rb}, %q{cookbooks/apt/recipes/default.rb}, %q{cookbooks/apt/resources/repository.rb}, %q{cookbooks/gems/recipes/default.rb}, %q{cookbooks/java/README.md}, %q{cookbooks/java/attributes/default.rb}, %q{cookbooks/java/files/default/java.seed}, %q{cookbooks/java/metadata.json}, %q{cookbooks/java/metadata.rb}, %q{cookbooks/java/recipes/default.rb}, %q{cookbooks/java/recipes/openjdk.rb}, %q{cookbooks/java/recipes/sun.rb}, %q{cookbooks/jruby/attributes/default.rb}, %q{cookbooks/jruby/recipes/default.rb}, %q{cookbooks/server/recipes/default.rb}, %q{cookbooks/server/templates/default/server.sh.erb}, %q{cookbooks/vagrant_main/recipes/default.rb}, %q{engineyard-visualvm-java.gemspec}, %q{engineyard-visualvm.gemspec}, %q{engineyard-visualvm.gemspec.in}, %q{ext/org/jruby/ext/jmx/Agent.java}, %q{ext/org/jruby/ext/jmx/JavaHome.java}, %q{ext/org/jruby/ext/jmx/RMIServerSocketFactoryImpl.java}, %q{lib/engineyard-visualvm.rb}, %q{lib/engineyard-visualvm/agent.jar}, %q{lib/engineyard-visualvm/cli.rb}, %q{lib/engineyard-visualvm/version.rb}, %q{spec/engineyard-visualvm_spec.rb}, %q{spec/spec_helper.rb}]
|
16
|
+
s.homepage = %q{https://github.com/engineyard/engineyard-visualvm}
|
17
|
+
s.require_paths = [%q{lib}]
|
18
|
+
s.rubyforge_project = %q{jruby-extras}
|
19
|
+
s.rubygems_version = %q{1.8.9}
|
20
|
+
s.summary = %q{Client and server helpers for using JMX and VisualVM with EY Cloud.}
|
21
|
+
s.test_files = [%q{spec/engineyard-visualvm_spec.rb}, %q{spec/spec_helper.rb}]
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<childprocess>, [">= 0"])
|
28
|
+
s.add_runtime_dependency(%q<engineyard>, [">= 0"])
|
29
|
+
s.add_runtime_dependency(%q<ffi-ncurses>, [">= 0"])
|
30
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<childprocess>, [">= 0"])
|
33
|
+
s.add_dependency(%q<engineyard>, [">= 0"])
|
34
|
+
s.add_dependency(%q<ffi-ncurses>, [">= 0"])
|
35
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
36
|
+
end
|
37
|
+
else
|
38
|
+
s.add_dependency(%q<childprocess>, [">= 0"])
|
39
|
+
s.add_dependency(%q<engineyard>, [">= 0"])
|
40
|
+
s.add_dependency(%q<ffi-ncurses>, [">= 0"])
|
41
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "engineyard-visualvm/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "engineyard-visualvm"
|
7
|
+
s.platform = "java" if defined?(use_jruby)
|
8
|
+
s.version = EngineYard::VisualVM::VERSION
|
9
|
+
s.authors = ["Nick Sieger"]
|
10
|
+
s.email = ["nick@nicksieger.com"]
|
11
|
+
s.homepage = "https://github.com/engineyard/engineyard-visualvm"
|
12
|
+
s.summary = %q{Client and server helpers for using JMX and VisualVM with EY Cloud.}
|
13
|
+
s.description = %q{This provides a Java agent and command-line utility to enable
|
14
|
+
JMX in any Java process such that it can be accessed through a firewall,
|
15
|
+
and a VisualVM launcher aid to connect to that process through ssh.}
|
16
|
+
|
17
|
+
s.rubyforge_project = "jruby-extras"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
|
24
|
+
s.add_runtime_dependency "childprocess"
|
25
|
+
s.add_runtime_dependency "engineyard"
|
26
|
+
if defined?(use_jruby)
|
27
|
+
s.add_runtime_dependency "jruby-openssl" # engineyard gem uses ssl
|
28
|
+
end
|
29
|
+
s.add_runtime_dependency "ffi-ncurses" # for highline gem, dep of engineyard
|
30
|
+
s.add_development_dependency "rspec"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Local Variables:
|
34
|
+
# mode: ruby
|
35
|
+
# End:
|
@@ -0,0 +1,169 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2011 Engine Yard, Inc.
|
3
|
+
* See the file LICENSE.txt included with the distribution for
|
4
|
+
* software license details.
|
5
|
+
*
|
6
|
+
* Based on CustomAgent.java found at the following URLs:
|
7
|
+
* https://blogs.oracle.com/jmxetc/entry/connecting_through_firewall_using_jmx
|
8
|
+
* https://blogs.oracle.com/jmxetc/entry/more_on_premain_and_jmx
|
9
|
+
*/
|
10
|
+
/*
|
11
|
+
* CustomAgent.java
|
12
|
+
*
|
13
|
+
* Copyright 2007, 2011 Sun Microsystems, Inc. All Rights Reserved.
|
14
|
+
*
|
15
|
+
* Redistribution and use in source and binary forms, with or without
|
16
|
+
* modification, are permitted provided that the following conditions
|
17
|
+
* are met:
|
18
|
+
*
|
19
|
+
* - Redistributions of source code must retain the above copyright
|
20
|
+
* notice, this list of conditions and the following disclaimer.
|
21
|
+
*
|
22
|
+
* - Redistributions in binary form must reproduce the above copyright
|
23
|
+
* notice, this list of conditions and the following disclaimer in the
|
24
|
+
* documentation and/or other materials provided with the distribution.
|
25
|
+
*
|
26
|
+
* - Neither the name of Sun Microsystems nor the names of its
|
27
|
+
* contributors may be used to endorse or promote products derived
|
28
|
+
* from this software without specific prior written permission.
|
29
|
+
*
|
30
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
31
|
+
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
32
|
+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
33
|
+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
34
|
+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
35
|
+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
36
|
+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
37
|
+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
38
|
+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
39
|
+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
40
|
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
41
|
+
*
|
42
|
+
* Created on Jul 25, 2007, 11:42:49 AM
|
43
|
+
*
|
44
|
+
*/
|
45
|
+
|
46
|
+
package org.jruby.ext.jmx;
|
47
|
+
|
48
|
+
import java.io.IOException;
|
49
|
+
import java.lang.management.ManagementFactory;
|
50
|
+
import java.rmi.Remote;
|
51
|
+
import java.rmi.registry.LocateRegistry;
|
52
|
+
import java.rmi.server.RMIServerSocketFactory;
|
53
|
+
import java.rmi.server.UnicastRemoteObject;
|
54
|
+
import java.util.HashMap;
|
55
|
+
import javax.management.MBeanServer;
|
56
|
+
import javax.management.remote.JMXConnectorServer;
|
57
|
+
import javax.management.remote.JMXConnectorServerFactory;
|
58
|
+
import javax.management.remote.JMXServiceURL;
|
59
|
+
import javax.management.remote.rmi.RMIConnectorServer;
|
60
|
+
|
61
|
+
import sun.jvmstat.monitor.remote.RemoteHost;
|
62
|
+
import sun.tools.jstatd.RemoteHostImpl;
|
63
|
+
import java.rmi.Naming;
|
64
|
+
|
65
|
+
public class Agent {
|
66
|
+
|
67
|
+
private static Thread cleaner;
|
68
|
+
|
69
|
+
private Agent() { }
|
70
|
+
|
71
|
+
public static void premain(String agentArgs) throws IOException {
|
72
|
+
|
73
|
+
// Ensure cryptographically strong random number generator used
|
74
|
+
// to choose the object number - see java.rmi.server.ObjID
|
75
|
+
//
|
76
|
+
System.setProperty("java.rmi.server.randomIDs", "true");
|
77
|
+
|
78
|
+
// Ensure JRuby JMX beans are available in all runtimes
|
79
|
+
System.setProperty("jruby.management.enabled", "true");
|
80
|
+
|
81
|
+
final int port = Integer.parseInt(System.getProperty("org.jruby.jmx.agent.port", "5900"));
|
82
|
+
final String hostname = System.getProperty("org.jruby.jmx.agent.hostname", "localhost");
|
83
|
+
|
84
|
+
// Make sure our RMI server knows which host we're binding to
|
85
|
+
System.setProperty("java.rmi.server.hostname", hostname);
|
86
|
+
System.setProperty("java.rmi.server.disableHttp", "true");
|
87
|
+
|
88
|
+
final RMIServerSocketFactory factory = new RMIServerSocketFactoryImpl(hostname);
|
89
|
+
|
90
|
+
LocateRegistry.createRegistry(port, null, factory);
|
91
|
+
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
92
|
+
HashMap<String,Object> env = new HashMap<String,Object>();
|
93
|
+
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, factory);
|
94
|
+
|
95
|
+
// Create an RMI connector server.
|
96
|
+
//
|
97
|
+
// As specified in the JMXServiceURL the RMIServer stub will be
|
98
|
+
// registered in the RMI registry running in the local host with the
|
99
|
+
// name "jmxrmi". This is the same name the out-of-the-box
|
100
|
+
// management agent uses to register the RMIServer stub too.
|
101
|
+
//
|
102
|
+
// The port specified in "service:jmx:rmi://"+hostname+":"+port
|
103
|
+
// is the second port, where RMI connection objects will be exported.
|
104
|
+
// Here we use the same port as that we choose for the RMI registry.
|
105
|
+
// The port for the RMI registry is specified in the second part
|
106
|
+
// of the URL, in "rmi://"+hostname+":"+port
|
107
|
+
//
|
108
|
+
JMXConnectorServer cs = new RMIConnectorServer(makeJMXServiceURL(hostname, port), env, mbs);
|
109
|
+
|
110
|
+
try {
|
111
|
+
// Create and register Jstatd remote host
|
112
|
+
Remote remoteHost = (Remote) Class.forName("sun.tools.jstatd.RemoteHostImpl").newInstance();
|
113
|
+
UnicastRemoteObject.exportObject(remoteHost, port, null, factory);
|
114
|
+
Naming.rebind("//"+hostname+":"+port+"/JStatRemoteHost", remoteHost);
|
115
|
+
} catch (Throwable e) {
|
116
|
+
System.err.println("Unable to start jstatd monitor: " + e.toString());
|
117
|
+
}
|
118
|
+
|
119
|
+
cs.start();
|
120
|
+
cleaner = new CleanThread(cs);
|
121
|
+
cleaner.start();
|
122
|
+
}
|
123
|
+
|
124
|
+
public static JMXServiceURL makeJMXServiceURL(String hostname, int port) throws IOException {
|
125
|
+
return new JMXServiceURL("service:jmx:rmi://"+hostname+
|
126
|
+
":"+port+"/jndi/rmi://"+hostname+":"+port+"/jmxrmi");
|
127
|
+
}
|
128
|
+
|
129
|
+
public static class CleanThread extends Thread {
|
130
|
+
private final JMXConnectorServer cs;
|
131
|
+
public CleanThread(JMXConnectorServer cs) {
|
132
|
+
super("JMX Agent Cleaner");
|
133
|
+
this.cs = cs;
|
134
|
+
setDaemon(true);
|
135
|
+
}
|
136
|
+
public void run() {
|
137
|
+
boolean loop = true;
|
138
|
+
try {
|
139
|
+
while (loop) {
|
140
|
+
final Thread[] all = new Thread[Thread.activeCount()+100];
|
141
|
+
final int count = Thread.enumerate(all);
|
142
|
+
loop = false;
|
143
|
+
for (int i=0;i<count;i++) {
|
144
|
+
final Thread t = all[i];
|
145
|
+
// daemon: skip it.
|
146
|
+
if (t.isDaemon()) continue;
|
147
|
+
// RMI Reaper: skip it.
|
148
|
+
if (t.getName().startsWith("RMI Reaper")) continue;
|
149
|
+
if (t.getName().startsWith("DestroyJavaVM")) continue;
|
150
|
+
// Non daemon, non RMI Reaper: join it, break the for
|
151
|
+
// loop, continue in the while loop (loop=true)
|
152
|
+
loop = true;
|
153
|
+
try {
|
154
|
+
t.join();
|
155
|
+
} catch (Exception ex) {
|
156
|
+
}
|
157
|
+
break;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
} catch (Exception ex) {
|
161
|
+
} finally {
|
162
|
+
try {
|
163
|
+
cs.stop();
|
164
|
+
} catch (Exception ex) {
|
165
|
+
}
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2011 Engine Yard, Inc.
|
3
|
+
* See the file LICENSE.txt included with the distribution for
|
4
|
+
* software license details.
|
5
|
+
*
|
6
|
+
*
|
7
|
+
* Cribbed from http://vafer.org/blog/20061010091658/
|
8
|
+
*/
|
9
|
+
|
10
|
+
package org.jruby.ext.jmx;
|
11
|
+
|
12
|
+
import java.io.IOException;
|
13
|
+
import java.net.InetAddress;
|
14
|
+
import java.net.ServerSocket;
|
15
|
+
import java.net.UnknownHostException;
|
16
|
+
import java.rmi.server.RMIServerSocketFactory;
|
17
|
+
import javax.net.ServerSocketFactory;
|
18
|
+
|
19
|
+
public class RMIServerSocketFactoryImpl implements RMIServerSocketFactory {
|
20
|
+
private final InetAddress localAddress;
|
21
|
+
|
22
|
+
public RMIServerSocketFactoryImpl(final String address) throws UnknownHostException {
|
23
|
+
localAddress = InetAddress.getByName(address);
|
24
|
+
}
|
25
|
+
|
26
|
+
public ServerSocket createServerSocket(final int port) throws IOException {
|
27
|
+
return ServerSocketFactory.getDefault().createServerSocket(port, 0, localAddress);
|
28
|
+
}
|
29
|
+
|
30
|
+
public boolean equals(Object obj) {
|
31
|
+
return obj != null && obj.getClass().equals(getClass());
|
32
|
+
}
|
33
|
+
|
34
|
+
public int hashCode() {
|
35
|
+
return RMIServerSocketFactoryImpl.class.hashCode();
|
36
|
+
}
|
37
|
+
}
|
Binary file
|
@@ -0,0 +1,225 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011 Engine Yard, Inc.
|
3
|
+
# See the file LICENSE.txt included with the distribution for
|
4
|
+
# software license details.
|
5
|
+
#++
|
6
|
+
|
7
|
+
require "thor"
|
8
|
+
require "childprocess"
|
9
|
+
require "socket"
|
10
|
+
require "engineyard"
|
11
|
+
require "engineyard/cli"
|
12
|
+
require "engineyard/thor"
|
13
|
+
|
14
|
+
module EngineYard
|
15
|
+
module VisualVM
|
16
|
+
module Helpers
|
17
|
+
def self.included(base)
|
18
|
+
unless base.instance_methods.map(&:to_s).include?("options")
|
19
|
+
base.class_eval do
|
20
|
+
def options
|
21
|
+
@_opts ||= { :host => "localhost" }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.port_available?(port)
|
28
|
+
begin
|
29
|
+
tcps = TCPServer.new("127.0.0.1", port)
|
30
|
+
true
|
31
|
+
rescue Errno::EADDRINUSE
|
32
|
+
false
|
33
|
+
ensure
|
34
|
+
tcps.close if tcps
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
STARTING_PORT = 5900
|
39
|
+
|
40
|
+
def self.next_free_port(start = STARTING_PORT)
|
41
|
+
(start...start+100).each do |i|
|
42
|
+
return i if port_available?(i)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def environment
|
47
|
+
@environment ||= begin
|
48
|
+
fetch_environment(options[:environment], options[:account]).tap {|env|
|
49
|
+
@user = env.username
|
50
|
+
@host = fetch_public_ip(env)
|
51
|
+
}
|
52
|
+
rescue EY::Error
|
53
|
+
raise if options[:environment]
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def user
|
59
|
+
@user
|
60
|
+
end
|
61
|
+
|
62
|
+
def ssh?
|
63
|
+
environment || (host && user) || options[:ssh] || options[:socks]
|
64
|
+
end
|
65
|
+
|
66
|
+
def socks_proxy?
|
67
|
+
options[:socks]
|
68
|
+
end
|
69
|
+
|
70
|
+
def ssh_host
|
71
|
+
user ? "#{user}@#{host}" : host
|
72
|
+
end
|
73
|
+
|
74
|
+
def host
|
75
|
+
@host ||= begin
|
76
|
+
match = /(.*)?@(.*)/.match options[:host]
|
77
|
+
if match
|
78
|
+
@user = match[1]
|
79
|
+
match[2]
|
80
|
+
else
|
81
|
+
@user = nil
|
82
|
+
options[:host]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def next_free_port
|
88
|
+
Helpers.next_free_port(port)
|
89
|
+
end
|
90
|
+
|
91
|
+
def port
|
92
|
+
@port ||= Numeric === options[:port] && options[:port] || STARTING_PORT
|
93
|
+
end
|
94
|
+
|
95
|
+
def jvm_arguments
|
96
|
+
tools_jar = find_tools_jar
|
97
|
+
args = "-Dorg.jruby.jmx.agent.port=#{next_free_port} -javaagent:#{agent_jar_path}"
|
98
|
+
args = "-Dorg.jruby.jmx.agent.hostname=#{host} #{args}" if host != "localhost"
|
99
|
+
args = "-Xbootclasspath/a:#{tools_jar} #{args}" if tools_jar
|
100
|
+
args
|
101
|
+
end
|
102
|
+
|
103
|
+
def jmx_service_url
|
104
|
+
"service:jmx:rmi://#{host}:#{port}/jndi/rmi://#{host}:#{port}/jmxrmi"
|
105
|
+
end
|
106
|
+
|
107
|
+
def find_executable?(exe)
|
108
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).detect do |path|
|
109
|
+
File.exist?(File.join(path, exe))
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def agent_jar_path
|
114
|
+
File.expand_path('../agent.jar', __FILE__)
|
115
|
+
end
|
116
|
+
|
117
|
+
def find_tools_jar
|
118
|
+
java_home = `java -classpath #{agent_jar_path} org.jruby.ext.jmx.agent.JavaHome`
|
119
|
+
[File.expand_path('./lib/tools.jar', java_home),
|
120
|
+
File.expand_path('../lib/tools.jar', java_home)].detect do |path|
|
121
|
+
File.readable?(path)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Return the public IP or instance hostname assigned to an
|
126
|
+
# environment (which may or may not be a booted cluster of
|
127
|
+
# instances) Displays error and exits if no public IP assigned
|
128
|
+
# to the environment
|
129
|
+
def fetch_public_ip(environment)
|
130
|
+
if environment.load_balancer_ip_address
|
131
|
+
return environment.load_balancer_ip_address
|
132
|
+
end
|
133
|
+
|
134
|
+
if environment.instances.length == 1 && environment.instances.first.public_hostname
|
135
|
+
return environment.instances.first.public_hostname
|
136
|
+
end
|
137
|
+
|
138
|
+
warn "#{environment.account.name}/#{environment.name} has no assigned public IP address or hostname."
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class CLI < Thor
|
144
|
+
include EY::UtilityMethods
|
145
|
+
include Helpers
|
146
|
+
|
147
|
+
class_option :host, :aliases => ["-H"], :default => "localhost",
|
148
|
+
:desc => "Host or IP where the JMX agent runs"
|
149
|
+
class_option :port, :aliases => ["-p"], :type => :numeric, :default => STARTING_PORT.to_s,
|
150
|
+
:desc => "Port where the JMX agent runs"
|
151
|
+
|
152
|
+
desc "jvmargs", "Print the arguments to be passed to the server JVM"
|
153
|
+
def jvmargs
|
154
|
+
puts jvm_arguments
|
155
|
+
end
|
156
|
+
|
157
|
+
desc "url", "Print the connection URL for the JMX server process"
|
158
|
+
def url
|
159
|
+
puts jmx_service_url
|
160
|
+
end
|
161
|
+
|
162
|
+
desc "version", "Show version"
|
163
|
+
def version
|
164
|
+
puts "ey-visualvm version #{EngineYard::VisualVM::VERSION}"
|
165
|
+
end
|
166
|
+
|
167
|
+
desc "start", "Launch VisualVM to connect to the server.\nUse either the environment/account or host/port options."
|
168
|
+
method_option :ssh, :type => :boolean, :desc => "Force VisualVM to connect through an ssh tunnel"
|
169
|
+
method_option :socks, :type => :boolean, :desc => "Force VisualVM to connect through a SOCKS proxy"
|
170
|
+
method_option :environment, :aliases => ["-e"], :desc => "Environment containing the IP to which to resolve", :type => :string
|
171
|
+
method_option :account, :aliases => ["-c"], :desc => "Name of the account where the environment is found"
|
172
|
+
def start
|
173
|
+
unless find_executable?("jvisualvm")
|
174
|
+
warn "Could not find \`jvisualvm\'; do you need to install the JDK?"
|
175
|
+
exit 1
|
176
|
+
end
|
177
|
+
|
178
|
+
visualvm_args = []
|
179
|
+
|
180
|
+
if ssh?
|
181
|
+
ssh_dest = ssh_host
|
182
|
+
unless system "ssh #{ssh_dest} true"
|
183
|
+
warn "Error establishing ssh connection; make sure you can `ssh #{ssh_dest}'."
|
184
|
+
exit 3
|
185
|
+
end
|
186
|
+
|
187
|
+
if socks_proxy?
|
188
|
+
proxy_port = next_free_port
|
189
|
+
visualvm_args += ["-J-Dnetbeans.system_socks_proxy=localhost:#{proxy_port}", "-J-Djava.net.useSystemProxies=true"]
|
190
|
+
@ssh_process = ChildProcess.build("ssh", "-ND", proxy_port.to_s, ssh_dest)
|
191
|
+
else
|
192
|
+
server_host, server_port = host, port
|
193
|
+
@host, @port = "localhost", next_free_port
|
194
|
+
@ssh_process = ChildProcess.build("ssh", "-NL", "#{@port}:#{@host}:#{server_port}", ssh_dest)
|
195
|
+
end
|
196
|
+
|
197
|
+
@ssh_process.start
|
198
|
+
end
|
199
|
+
|
200
|
+
visualvm_args += ["--openjmx", jmx_service_url.to_s]
|
201
|
+
visualvm = ChildProcess.build("jvisualvm", *visualvm_args)
|
202
|
+
visualvm.start
|
203
|
+
|
204
|
+
loop do
|
205
|
+
visualvm.exited? && break
|
206
|
+
sleep 1
|
207
|
+
end
|
208
|
+
|
209
|
+
@ssh_process.stop if @ssh_process
|
210
|
+
rescue EY::Error => e
|
211
|
+
warn e.message
|
212
|
+
exit 2
|
213
|
+
end
|
214
|
+
|
215
|
+
def help(task = nil, *args)
|
216
|
+
unless task
|
217
|
+
puts "usage: ey-visualvm <task> [options|arguments]"
|
218
|
+
puts "Make JMX and VisualVM more accessible to your server-side JVM."
|
219
|
+
puts
|
220
|
+
end
|
221
|
+
super
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|