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.
Files changed (46) hide show
  1. data/.gitignore +5 -0
  2. data/ChangeLog +22 -0
  3. data/Gemfile +26 -0
  4. data/LICENSE.txt +23 -0
  5. data/README.md +104 -0
  6. data/Rakefile +124 -0
  7. data/Vagrantfile +94 -0
  8. data/bin/ey-visualvm +9 -0
  9. data/cookbooks/apt/README.md +122 -0
  10. data/cookbooks/apt/files/default/apt-cacher +9 -0
  11. data/cookbooks/apt/files/default/apt-cacher.conf +144 -0
  12. data/cookbooks/apt/files/default/apt-proxy-v2.conf +50 -0
  13. data/cookbooks/apt/metadata.json +34 -0
  14. data/cookbooks/apt/metadata.rb +13 -0
  15. data/cookbooks/apt/providers/repository.rb +73 -0
  16. data/cookbooks/apt/recipes/cacher-client.rb +44 -0
  17. data/cookbooks/apt/recipes/cacher.rb +45 -0
  18. data/cookbooks/apt/recipes/default.rb +50 -0
  19. data/cookbooks/apt/resources/repository.rb +30 -0
  20. data/cookbooks/gems/recipes/default.rb +16 -0
  21. data/cookbooks/java/README.md +102 -0
  22. data/cookbooks/java/attributes/default.rb +29 -0
  23. data/cookbooks/java/files/default/java.seed +11 -0
  24. data/cookbooks/java/metadata.json +50 -0
  25. data/cookbooks/java/metadata.rb +16 -0
  26. data/cookbooks/java/recipes/default.rb +21 -0
  27. data/cookbooks/java/recipes/openjdk.rb +39 -0
  28. data/cookbooks/java/recipes/sun.rb +93 -0
  29. data/cookbooks/jruby/attributes/default.rb +2 -0
  30. data/cookbooks/jruby/recipes/default.rb +24 -0
  31. data/cookbooks/server/recipes/default.rb +9 -0
  32. data/cookbooks/server/templates/default/server.sh.erb +27 -0
  33. data/cookbooks/vagrant_main/recipes/default.rb +4 -0
  34. data/engineyard-visualvm-java.gemspec +47 -0
  35. data/engineyard-visualvm.gemspec +43 -0
  36. data/engineyard-visualvm.gemspec.in +35 -0
  37. data/ext/org/jruby/ext/jmx/Agent.java +169 -0
  38. data/ext/org/jruby/ext/jmx/JavaHome.java +7 -0
  39. data/ext/org/jruby/ext/jmx/RMIServerSocketFactoryImpl.java +37 -0
  40. data/lib/engineyard-visualvm.rb +8 -0
  41. data/lib/engineyard-visualvm/agent.jar +0 -0
  42. data/lib/engineyard-visualvm/cli.rb +225 -0
  43. data/lib/engineyard-visualvm/version.rb +11 -0
  44. data/spec/engineyard-visualvm_spec.rb +202 -0
  45. data/spec/spec_helper.rb +49 -0
  46. metadata +157 -0
@@ -0,0 +1,9 @@
1
+ template "#{ENV['HOME']}/server.sh" do
2
+ source "server.sh.erb"
3
+ mode "0755"
4
+ end
5
+
6
+ execute "start server" do
7
+ cwd ENV['HOME']
8
+ command "./server.sh"
9
+ end
@@ -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,4 @@
1
+ require_recipe 'apt'
2
+ require_recipe 'jruby'
3
+ require_recipe 'gems'
4
+ require_recipe 'server'
@@ -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,7 @@
1
+ package org.jruby.ext.jmx.agent;
2
+
3
+ public class JavaHome {
4
+ public static void main(String[] args) {
5
+ System.out.println(System.getProperty("java.home"));
6
+ }
7
+ }
@@ -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
+ }
@@ -0,0 +1,8 @@
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 'engineyard-visualvm/version'
8
+ require 'engineyard-visualvm/cli'
@@ -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