jmx 0.1
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/LICENSE.txt +20 -0
- data/Manifest.txt +13 -0
- data/README.txt +29 -0
- data/Rakefile +27 -0
- data/lib/jmx.rb +210 -0
- data/lib/jmx/dynamic_mbean.rb +101 -0
- data/lib/jmx/server.rb +117 -0
- data/lib/jmx/version.rb +3 -0
- data/lib/rmi.rb +21 -0
- data/samples/memory.rb +29 -0
- data/test/jmx_client_test.rb +88 -0
- data/test/jmx_server_test.rb +71 -0
- metadata +68 -0
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2008 Thomas E Enebo <enebo@acm.org>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
data/README.txt
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
= JMX
|
|
2
|
+
|
|
3
|
+
== DESCRIPTION:
|
|
4
|
+
|
|
5
|
+
JMX is a library which allows you to access JMX MBeans as a client or create
|
|
6
|
+
your own MBeans as a Ruby class.
|
|
7
|
+
|
|
8
|
+
http://jruby-extras.rubyforge.org/jmx/
|
|
9
|
+
|
|
10
|
+
== FEATURES/PROBLEMS:
|
|
11
|
+
|
|
12
|
+
* Use '-J-Dcom.sun.management.jmxremote' to make jruby process accessible from a jruby command-line
|
|
13
|
+
|
|
14
|
+
== SYNOPSIS:
|
|
15
|
+
|
|
16
|
+
require 'jmx'
|
|
17
|
+
|
|
18
|
+
client = JMX.simple_connect(:port => 9999)
|
|
19
|
+
|
|
20
|
+
memory = client["java.lang:type=Memory"]
|
|
21
|
+
puts memory.attributes
|
|
22
|
+
|
|
23
|
+
== REQUIREMENTS:
|
|
24
|
+
|
|
25
|
+
* JRuby
|
|
26
|
+
|
|
27
|
+
== INSTALL:
|
|
28
|
+
|
|
29
|
+
* jruby -S gem install jmx
|
data/Rakefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MANIFEST = FileList["Manifest.txt", "Rakefile", "README.txt", "LICENSE.txt", "lib/**/*", "samples/*","test/**/*"]
|
|
2
|
+
|
|
3
|
+
file "Manifest.txt" => :manifest
|
|
4
|
+
task :manifest do
|
|
5
|
+
File.open("Manifest.txt", "w") {|f| MANIFEST.each {|n| f << "#{n}\n"} }
|
|
6
|
+
end
|
|
7
|
+
Rake::Task['manifest'].invoke # Always regen manifest, so Hoe has up-to-date list of files
|
|
8
|
+
|
|
9
|
+
$LOAD_PATH << 'lib'
|
|
10
|
+
require 'jmx/version'
|
|
11
|
+
begin
|
|
12
|
+
require 'hoe'
|
|
13
|
+
Hoe.new("jmx", JMX::VERSION) do |p|
|
|
14
|
+
p.rubyforge_name = "jruby-extras"
|
|
15
|
+
p.url = "http://jruby-extras.rubyforge.org/jmx"
|
|
16
|
+
p.author = "Thomas Enebo"
|
|
17
|
+
p.email = "enebo@acm.org"
|
|
18
|
+
p.summary = "Package for interacting/creating Java Management Extensions"
|
|
19
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
|
20
|
+
p.description = "Install this gem and require 'jmx' to load the library."
|
|
21
|
+
end.spec.dependencies.delete_if { |dep| dep.name == "hoe" }
|
|
22
|
+
rescue LoadError
|
|
23
|
+
puts "You need Hoe installed to be able to package this gem"
|
|
24
|
+
rescue => e
|
|
25
|
+
p e.backtrace
|
|
26
|
+
puts "ignoring error while loading hoe: #{e.to_s}"
|
|
27
|
+
end
|
data/lib/jmx.rb
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
include Java
|
|
2
|
+
|
|
3
|
+
require 'rmi'
|
|
4
|
+
require 'jmx/dynamic_mbean'
|
|
5
|
+
require 'jmx/server'
|
|
6
|
+
|
|
7
|
+
import java.util.ArrayList
|
|
8
|
+
import javax.management.Attribute
|
|
9
|
+
import javax.management.DynamicMBean
|
|
10
|
+
import javax.management.MBeanInfo
|
|
11
|
+
import javax.management.ObjectName
|
|
12
|
+
|
|
13
|
+
class ObjectName
|
|
14
|
+
def [](key)
|
|
15
|
+
get_key_property(key.to_s)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def info(server)
|
|
19
|
+
server.getMBeanInfo(self)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module javax::management::openmbean::CompositeData
|
|
24
|
+
include Enumerable
|
|
25
|
+
|
|
26
|
+
def [](key)
|
|
27
|
+
get(key.to_s)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def method_missing(name, *args)
|
|
31
|
+
self[name]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def each
|
|
35
|
+
get_composite_type.key_set.each { |key| yield key }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def each_pair
|
|
39
|
+
get_composite_type.key_set.each { |key| yield key, get(key) }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
module JMX
|
|
44
|
+
##
|
|
45
|
+
# Connect to a MBeanServer
|
|
46
|
+
# opts can contain several values
|
|
47
|
+
# :host - The hostname where the server resides (def: localhost)
|
|
48
|
+
# :port - Which port the server resides at (def: 8686)
|
|
49
|
+
# :url_path - path part of JMXServerURL (def: /jmxrmi)
|
|
50
|
+
# :user - User to connect as (optional)
|
|
51
|
+
# :password - Password for user (optional)
|
|
52
|
+
def self.connect(opts = {})
|
|
53
|
+
host = opts[:host] || 'localhost'
|
|
54
|
+
port = opts[:port] || 8686
|
|
55
|
+
url_path = opts[:url_path] || "/jmxrmi"
|
|
56
|
+
url = "service:jmx:rmi:///jndi/rmi://#{host}:#{port}#{url_path}"
|
|
57
|
+
|
|
58
|
+
if opts[:user]
|
|
59
|
+
JMX::MBeanServer.new url, opts[:user], opts[:password]
|
|
60
|
+
else
|
|
61
|
+
JMX::MBeanServer.new url
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# sad little simple server setup so you can connect up to it.
|
|
67
|
+
#
|
|
68
|
+
def self.simple_server(opts = {})
|
|
69
|
+
port = opts[:port] || 8686
|
|
70
|
+
url_path = opts[:url_path] || "/jmxrmi"
|
|
71
|
+
url = "service:jmx:rmi:///jndi/rmi://localhost:#{port}#{url_path}"
|
|
72
|
+
$registry = RMIRegistry.new port
|
|
73
|
+
@connector = JMX::MBeanServerConnector.new(url, JMX::MBeanServer.new).start
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Holder for beans created from retrieval (namespace protection [tm]).
|
|
77
|
+
# This also gives MBeans nicer names when inspected
|
|
78
|
+
module MBeans
|
|
79
|
+
##
|
|
80
|
+
# Create modules in this namespace for each package in the Java fully
|
|
81
|
+
# qualified name and return the deepest module along with the Java class
|
|
82
|
+
# name back to the caller.
|
|
83
|
+
def self.parent_for(java_class_fqn)
|
|
84
|
+
java_class_fqn.split(".").inject(MBeans) do |parent, segment|
|
|
85
|
+
# Note: We are boned if java class name is lower cased
|
|
86
|
+
return [parent, segment] if segment =~ /^[A-Z]/
|
|
87
|
+
|
|
88
|
+
segment.capitalize!
|
|
89
|
+
unless parent.const_defined? segment
|
|
90
|
+
parent.const_set segment, Module.new
|
|
91
|
+
else
|
|
92
|
+
parent.const_get segment
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Create a Ruby proxy based on the MBean represented by the object_name
|
|
100
|
+
class MBeanProxy
|
|
101
|
+
# Generate a friendly Ruby proxy for the MBean represented by object_name
|
|
102
|
+
def self.generate(server, object_name)
|
|
103
|
+
parent, class_name = MBeans.parent_for object_name.info(server).class_name
|
|
104
|
+
|
|
105
|
+
if parent.const_defined? class_name
|
|
106
|
+
proxy = parent.const_get(class_name)
|
|
107
|
+
else
|
|
108
|
+
proxy = Class.new MBeanProxy
|
|
109
|
+
parent.const_set class_name, proxy
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
proxy.new(server, object_name)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def initialize(server, object_name)
|
|
116
|
+
@server, @object_name = server, object_name
|
|
117
|
+
@info = @server.getMBeanInfo(@object_name)
|
|
118
|
+
|
|
119
|
+
define_attributes
|
|
120
|
+
define_operations
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def attributes
|
|
124
|
+
@attributes ||= @info.attributes.inject([]) { |s,attr| s << attr.name }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def operations
|
|
128
|
+
@operations ||= @info.operations.inject([]) { |s,op| s << op.name }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Get MBean attribute specified by name
|
|
132
|
+
def [](name)
|
|
133
|
+
@server.getAttribute @object_name, name.to_s
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Set MBean attribute specified by name to value
|
|
137
|
+
def []=(name, value)
|
|
138
|
+
@server.setAttribute @object_name, Attribute.new(name.to_s, value)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def add_notification_listener(filter=nil, handback=nil, &listener)
|
|
142
|
+
@server.addNotificationListener @object_name, listener, filter, handback
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def remove_notification_listener(listener)
|
|
146
|
+
@server.removeNotificationListener @object_name, listener
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def method_missing(name, *args)
|
|
150
|
+
puts "Invoking: #{name}, #{args}"
|
|
151
|
+
java_args = java_args(args)
|
|
152
|
+
@server.invoke @object_name, name.to_s, java_args, java_types(java_args)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
private
|
|
156
|
+
|
|
157
|
+
# Define ruby friendly methods for attributes. For odd attribute names or names
|
|
158
|
+
# that you want to call with the actual attribute name you can call aref/aset
|
|
159
|
+
def define_attributes
|
|
160
|
+
@info.attributes.each do |attr|
|
|
161
|
+
rname = underscore(attr.name)
|
|
162
|
+
self.class.__send__(:define_method, rname) { self[attr.name] } if attr.readable?
|
|
163
|
+
self.class.__send__(:define_method, rname + "=") {|v| self[attr.name] = v } if attr.writable?
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def define_operations
|
|
168
|
+
@info.operations.each do |op|
|
|
169
|
+
self.class.__send__(:define_method, op.name) do |*args|
|
|
170
|
+
jargs = java_args(op.signature, args)
|
|
171
|
+
@server.invoke @object_name, op.name, jargs, java_types(jargs)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Given the signature and the parameters supplied do these signatures match.
|
|
177
|
+
# Repackage these parameters as Java objects in a primitive object array.
|
|
178
|
+
def java_args(signature, params)
|
|
179
|
+
return nil if params.nil?
|
|
180
|
+
|
|
181
|
+
i = 0
|
|
182
|
+
params.map do |param|
|
|
183
|
+
required_type = JavaClass.for_name(signature[i].get_type)
|
|
184
|
+
java_arg = Java.ruby_to_java(param)
|
|
185
|
+
|
|
186
|
+
if (param.kind_of? Array)
|
|
187
|
+
java_arg = param.inject(ArrayList.new) {|l, element| l << element }
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
arg_type = java_arg.java_class
|
|
191
|
+
|
|
192
|
+
raise TypeError.new("parameter #{signature[i].name} expected to be #{required_type}, but was #{arg_type}") if !required_type.assignable_from? arg_type
|
|
193
|
+
i = i + 1
|
|
194
|
+
|
|
195
|
+
java_arg
|
|
196
|
+
end.to_java(:object)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Convert a collection of java objects to their Java class name equivalents
|
|
200
|
+
def java_types(params)
|
|
201
|
+
return nil if params.nil?
|
|
202
|
+
|
|
203
|
+
params.map {|e| params.java_class.name }.to_java(:string)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def underscore(string)
|
|
207
|
+
string.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module JMX
|
|
2
|
+
import javax.management.MBeanParameterInfo
|
|
3
|
+
import javax.management.MBeanOperationInfo
|
|
4
|
+
import javax.management.MBeanInfo
|
|
5
|
+
|
|
6
|
+
module JavaTypeAware
|
|
7
|
+
SIMPLE_TYPES = {
|
|
8
|
+
:int => 'java.lang.Integer',
|
|
9
|
+
:list => 'java.util.List',
|
|
10
|
+
:long => 'java.lang.Long',
|
|
11
|
+
:map => 'java.util.Map',
|
|
12
|
+
:set => 'java.util.Set',
|
|
13
|
+
:string => 'java.lang.String',
|
|
14
|
+
:void => 'java.lang.Void'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def to_java_type(type_name)
|
|
18
|
+
SIMPLE_TYPES[type_name] || type_name
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Parameter
|
|
23
|
+
include JavaTypeAware
|
|
24
|
+
|
|
25
|
+
def initialize(type, name, description)
|
|
26
|
+
@type, @name, @description = type, name, description
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_jmx
|
|
30
|
+
MBeanParameterInfo.new @name.to_s, to_java_type(@type), @description
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class Operation < Struct.new(:description, :parameters, :return_type, :name, :impact)
|
|
35
|
+
include JavaTypeAware
|
|
36
|
+
|
|
37
|
+
def initialize(description)
|
|
38
|
+
super
|
|
39
|
+
self.parameters, self.impact, self.description = [], MBeanOperationInfo::UNKNOWN, description
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_jmx
|
|
43
|
+
java_parameters = parameters.map { |parameter| parameter.to_jmx }
|
|
44
|
+
MBeanOperationInfo.new name.to_s, description, java_parameters.to_java(javax.management.MBeanParameterInfo), to_java_type(return_type), impact
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class RubyDynamicMBean
|
|
50
|
+
import javax.management.MBeanOperationInfo
|
|
51
|
+
|
|
52
|
+
# TODO: preserve any original method_added?
|
|
53
|
+
# TODO: Error handling here when it all goes wrong?
|
|
54
|
+
def self.method_added(name)
|
|
55
|
+
return if Thread.current[:op].nil?
|
|
56
|
+
Thread.current[:op].name = name
|
|
57
|
+
operations << Thread.current[:op].to_jmx
|
|
58
|
+
Thread.current[:op] = nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.attributes
|
|
62
|
+
Thread.current[:attrs] ||= []
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.operations
|
|
66
|
+
Thread.current[:ops] ||= []
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Last operation wins if more than one
|
|
70
|
+
def self.operation(description)
|
|
71
|
+
include DynamicMBean
|
|
72
|
+
|
|
73
|
+
# Wait to error check until method_added so we can know method name
|
|
74
|
+
Thread.current[:op] = JMX::Operation.new description
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.parameter(type, name=nil, description=nil)
|
|
78
|
+
Thread.current[:op].parameters << JMX::Parameter.new(type, name, description)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def self.returns(type)
|
|
82
|
+
Thread.current[:op].return_type = type
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def initialize(name, description)
|
|
86
|
+
operations = self.class.operations.to_java(MBeanOperationInfo)
|
|
87
|
+
@info = MBeanInfo.new name, description, nil, nil, operations, nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def getAttribute(attribute); $stderr.puts "getAttribute"; end
|
|
91
|
+
def getAttributes(attributes); $stderr.puts "getAttributes"; end
|
|
92
|
+
def getMBeanInfo; @info; end
|
|
93
|
+
def invoke(actionName, params=nil, signature=nil)
|
|
94
|
+
send(actionName, *params)
|
|
95
|
+
end
|
|
96
|
+
def setAttribute(attribute); $stderr.puts "setAttribute"; end
|
|
97
|
+
def setAttributes(attributes); $stderr.puts "setAttributes"; end
|
|
98
|
+
def to_s; toString; end
|
|
99
|
+
def inspect; toString; end
|
|
100
|
+
def toString; "#@info.class_name: #@info.description"; end
|
|
101
|
+
end
|
data/lib/jmx/server.rb
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
module JMX
|
|
2
|
+
# Represents both MBeanServer and MBeanServerConnection
|
|
3
|
+
class MBeanServer
|
|
4
|
+
import javax.management.Attribute
|
|
5
|
+
import javax.management.MBeanServerFactory
|
|
6
|
+
import javax.management.remote.JMXConnectorFactory
|
|
7
|
+
import javax.management.remote.JMXServiceURL
|
|
8
|
+
|
|
9
|
+
attr_accessor :server
|
|
10
|
+
@@classes = {}
|
|
11
|
+
|
|
12
|
+
def initialize(location=nil, username=nil, password=nil)
|
|
13
|
+
if (location)
|
|
14
|
+
env = username ?
|
|
15
|
+
{"jmx.remote.credentials" => [username, password].to_java(:string)} :
|
|
16
|
+
nil
|
|
17
|
+
url = JMXServiceURL.new location
|
|
18
|
+
@server = JMXConnectorFactory.connect(url, env).getMBeanServerConnection
|
|
19
|
+
else
|
|
20
|
+
@server = java.lang.management.ManagementFactory.getPlatformMBeanServer
|
|
21
|
+
#@server = MBeanServerFactory.createMBeanServer
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def [](object_name)
|
|
26
|
+
name = make_object_name object_name
|
|
27
|
+
|
|
28
|
+
unless @server.isRegistered(name)
|
|
29
|
+
raise NoSuchBeanError.new("No name: #{object_name}")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#### TODO: Why?
|
|
33
|
+
@server.getObjectInstance name
|
|
34
|
+
MBeanProxy.generate(@server, name)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def []=(class_name, object_name)
|
|
38
|
+
name = make_object_name object_name
|
|
39
|
+
|
|
40
|
+
@server.createMBean class_name, name, nil, nil
|
|
41
|
+
|
|
42
|
+
MBeanProxy.generate(@server, name)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def default_domain
|
|
46
|
+
@server.getDefaultDomain
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def domains
|
|
50
|
+
@server.domains
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def mbean_count
|
|
54
|
+
@server.getMBeanCount
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def query_names(name=nil, query=nil)
|
|
58
|
+
object_name = name.nil? ? nil : make_object_name(name)
|
|
59
|
+
|
|
60
|
+
@server.query_names(object_name, query)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def register_mbean(object, object_name)
|
|
64
|
+
name = make_object_name object_name
|
|
65
|
+
|
|
66
|
+
@server.registerMBean(object, name)
|
|
67
|
+
|
|
68
|
+
MBeanProxy.generate(@server, name)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.find(agent_id=nil)
|
|
72
|
+
MBeanServerFactory.findMBeanServer(agent_id)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def make_object_name(object_name)
|
|
78
|
+
return object_name if object_name.kind_of? ObjectName
|
|
79
|
+
|
|
80
|
+
ObjectName.new object_name
|
|
81
|
+
rescue
|
|
82
|
+
raise ArgumentError.new("Invalid ObjectName #{$!.message}")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class NoSuchBeanError < RuntimeError
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class MBeanServerConnector
|
|
90
|
+
import javax.management.remote.JMXServiceURL
|
|
91
|
+
import javax.management.remote.JMXConnectorServerFactory
|
|
92
|
+
|
|
93
|
+
def initialize(location, server)
|
|
94
|
+
@url = JMXServiceURL.new location
|
|
95
|
+
@server = JMXConnectorServerFactory.newJMXConnectorServer @url, nil, server.server
|
|
96
|
+
|
|
97
|
+
if block_given?
|
|
98
|
+
start
|
|
99
|
+
yield
|
|
100
|
+
stop
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def active?
|
|
105
|
+
@server.isActive
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def start
|
|
109
|
+
@server.start
|
|
110
|
+
self
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def stop
|
|
114
|
+
@server.stop if active?
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
data/lib/jmx/version.rb
ADDED
data/lib/rmi.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
include Java
|
|
2
|
+
|
|
3
|
+
import java.rmi.registry.LocateRegistry
|
|
4
|
+
import java.rmi.registry.Registry
|
|
5
|
+
import java.rmi.server.UnicastRemoteObject
|
|
6
|
+
|
|
7
|
+
class RMIRegistry
|
|
8
|
+
def initialize(port = Registry::REGISTRY_PORT)
|
|
9
|
+
start(port)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def start(port)
|
|
13
|
+
@registry = LocateRegistry.createRegistry port
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def stop
|
|
18
|
+
UnicastRemoteObject.unexportObject @registry, true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
data/samples/memory.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'jmx'
|
|
2
|
+
|
|
3
|
+
def in_mb(value)
|
|
4
|
+
format "%0.2f Mb" % (value.to_f / (1024 * 1024))
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
server = JMX.simple_server
|
|
8
|
+
client = JMX.connect
|
|
9
|
+
memory = client["java.lang:type=Memory"]
|
|
10
|
+
|
|
11
|
+
Thread.new do
|
|
12
|
+
puts "Enter 'gc' to garbage collect or anything else to quit"
|
|
13
|
+
while (command = gets.chomp)
|
|
14
|
+
break if command != "gc"
|
|
15
|
+
memory.gc
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
server.stop
|
|
19
|
+
exit 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
while (true)
|
|
23
|
+
heap = in_mb(memory.heap_memory_usage.used)
|
|
24
|
+
non_heap = in_mb(memory.non_heap_memory_usage.used)
|
|
25
|
+
|
|
26
|
+
puts "Heap: #{heap}, Non-Heap: #{non_heap}"
|
|
27
|
+
sleep(2)
|
|
28
|
+
end
|
|
29
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# In order to run these tests you must be running GFv2
|
|
2
|
+
|
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
|
4
|
+
|
|
5
|
+
require 'test/unit'
|
|
6
|
+
require 'rmi'
|
|
7
|
+
require 'jmx'
|
|
8
|
+
|
|
9
|
+
PORT = 9999
|
|
10
|
+
$registry = RMIRegistry.new PORT
|
|
11
|
+
|
|
12
|
+
class JMXConnectorClientTest < Test::Unit::TestCase
|
|
13
|
+
URL = "service:jmx:rmi:///jndi/rmi://localhost:#{PORT}/jmxrmi"
|
|
14
|
+
|
|
15
|
+
def setup
|
|
16
|
+
@connector = JMX::MBeanServerConnector.new(URL, JMX::MBeanServer.new)
|
|
17
|
+
@connector.start
|
|
18
|
+
@client = JMX::connect(:port => PORT)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def teardown
|
|
22
|
+
@connector.stop
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_invalid_mbean_name
|
|
26
|
+
assert_raises(ArgumentError) { @client["::::::"] }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_get_mbean
|
|
30
|
+
memory = @client["java.lang:type=Memory"]
|
|
31
|
+
|
|
32
|
+
assert_not_nil memory, "Could not acquire memory mbean"
|
|
33
|
+
|
|
34
|
+
# Attr form
|
|
35
|
+
heap = memory[:HeapMemoryUsage]
|
|
36
|
+
assert_not_nil heap
|
|
37
|
+
assert(heap[:used] > 0, "No heap used? Impossible!")
|
|
38
|
+
|
|
39
|
+
# underscored form
|
|
40
|
+
heap = memory.heap_memory_usage
|
|
41
|
+
assert_not_nil heap
|
|
42
|
+
assert(heap.used > 0, "No heap used? Impossible!")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_set_mbean
|
|
46
|
+
memory = @client["java.lang:type=Memory"]
|
|
47
|
+
original_verbose = memory.verbose
|
|
48
|
+
memory.verbose = !original_verbose
|
|
49
|
+
assert(memory.verbose != original_verbose, "Could not change verbose")
|
|
50
|
+
|
|
51
|
+
memory[:Verbose] = original_verbose
|
|
52
|
+
assert(memory[:Verbose] == original_verbose, "Could not change back verbose")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_attributes
|
|
56
|
+
memory = @client["java.lang:type=Memory"]
|
|
57
|
+
assert(memory.attributes.include?("HeapMemoryUsage"), "HeapMemoryUsage not found")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_operations
|
|
61
|
+
memory = @client["java.lang:type=Memory"]
|
|
62
|
+
assert(memory.operations.include?("gc"), "gc not found")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_simple_operation
|
|
67
|
+
memory = @client["java.lang:type=Memory"]
|
|
68
|
+
|
|
69
|
+
heap1 = memory[:HeapMemoryUsage][:used]
|
|
70
|
+
memory.gc
|
|
71
|
+
heap2 = memory[:HeapMemoryUsage][:used]
|
|
72
|
+
|
|
73
|
+
assert(heap1.to_i >= heap2.to_i, "GC did not collect")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_query_names
|
|
77
|
+
names = @client.query_names("java.lang:type=MemoryPool,*")
|
|
78
|
+
assert(names.size > 0, "No memory pools. Impossible!")
|
|
79
|
+
|
|
80
|
+
a_memory_pool_bean = @client[names.to_array[0]]
|
|
81
|
+
assert_not_nil a_memory_pool_bean, "Name must resolve to something"
|
|
82
|
+
|
|
83
|
+
usage = a_memory_pool_bean[:Usage]
|
|
84
|
+
assert_not_nil usage, "Memory pools have usage"
|
|
85
|
+
|
|
86
|
+
assert_not_nil usage[:used], "Some memory is used"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#
|
|
2
|
+
# To change this template, choose Tools | Templates
|
|
3
|
+
# and open the template in the editor.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
|
7
|
+
|
|
8
|
+
require 'test/unit'
|
|
9
|
+
require 'rmi'
|
|
10
|
+
require 'jmx'
|
|
11
|
+
|
|
12
|
+
class MyDynamicMBean < RubyDynamicMBean
|
|
13
|
+
operation "Doubles a value"
|
|
14
|
+
parameter :int, "a", "Value to double"
|
|
15
|
+
returns :int
|
|
16
|
+
def double(a)
|
|
17
|
+
a + a
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
operation "Doubles a string"
|
|
21
|
+
parameter :string, "a", "Value to double"
|
|
22
|
+
returns :string
|
|
23
|
+
def string_double(a)
|
|
24
|
+
a + a
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
operation "Give me foo"
|
|
28
|
+
returns :string
|
|
29
|
+
def foo
|
|
30
|
+
"foo"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
operation "Concatentates a list"
|
|
34
|
+
parameter :list, "list", "List to concatenate"
|
|
35
|
+
returns :string
|
|
36
|
+
def concat(list)
|
|
37
|
+
list.inject("") { |memo, element| memo << element.to_s }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class JMXServerTest < Test::Unit::TestCase
|
|
42
|
+
PORT = 9999
|
|
43
|
+
URL = "service:jmx:rmi:///jndi/rmi://localhost:#{PORT}/jmxrmi"
|
|
44
|
+
|
|
45
|
+
def setup
|
|
46
|
+
@registry = RMIRegistry.new PORT
|
|
47
|
+
@server = JMX::MBeanServer.new
|
|
48
|
+
@connector = JMX::MBeanServerConnector.new(URL, @server)
|
|
49
|
+
@connector.start
|
|
50
|
+
@client = JMX::connect(:port => PORT)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def teardown
|
|
54
|
+
@connector.stop
|
|
55
|
+
@registry.stop
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_ruby_mbean
|
|
59
|
+
dyna = MyDynamicMBean.new("domain.MySuperBean", "Heh")
|
|
60
|
+
domain = @server.default_domain
|
|
61
|
+
@server.register_mbean dyna, "#{domain}:type=MyDynamicMBean"
|
|
62
|
+
|
|
63
|
+
# Get bean from client connector connection
|
|
64
|
+
bean = @client["#{domain}:type=MyDynamicMBean"]
|
|
65
|
+
assert_equal("foo", bean.foo)
|
|
66
|
+
assert_equal(6, bean.double(3))
|
|
67
|
+
assert_raise(TypeError) { puts bean.double("HEH") }
|
|
68
|
+
assert_equal("hehheh", bean.string_double("heh"))
|
|
69
|
+
assert_equal("123", bean.concat([1,2,3]))
|
|
70
|
+
end
|
|
71
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: jmx
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: "0.1"
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Thomas Enebo
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2008-06-06 00:00:00 -05:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description: Install this gem and require 'jmx' to load the library.
|
|
17
|
+
email: enebo@acm.org
|
|
18
|
+
executables: []
|
|
19
|
+
|
|
20
|
+
extensions: []
|
|
21
|
+
|
|
22
|
+
extra_rdoc_files:
|
|
23
|
+
- Manifest.txt
|
|
24
|
+
- README.txt
|
|
25
|
+
- LICENSE.txt
|
|
26
|
+
files:
|
|
27
|
+
- Manifest.txt
|
|
28
|
+
- Rakefile
|
|
29
|
+
- README.txt
|
|
30
|
+
- LICENSE.txt
|
|
31
|
+
- lib/jmx
|
|
32
|
+
- lib/jmx/dynamic_mbean.rb
|
|
33
|
+
- lib/jmx/server.rb
|
|
34
|
+
- lib/jmx/version.rb
|
|
35
|
+
- lib/jmx.rb
|
|
36
|
+
- lib/rmi.rb
|
|
37
|
+
- samples/memory.rb
|
|
38
|
+
- test/jmx_client_test.rb
|
|
39
|
+
- test/jmx_server_test.rb
|
|
40
|
+
has_rdoc: true
|
|
41
|
+
homepage: http://jruby-extras.rubyforge.org/jmx
|
|
42
|
+
post_install_message:
|
|
43
|
+
rdoc_options:
|
|
44
|
+
- --main
|
|
45
|
+
- README.txt
|
|
46
|
+
require_paths:
|
|
47
|
+
- lib
|
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: "0"
|
|
53
|
+
version:
|
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: "0"
|
|
59
|
+
version:
|
|
60
|
+
requirements: []
|
|
61
|
+
|
|
62
|
+
rubyforge_project: jruby-extras
|
|
63
|
+
rubygems_version: 1.0.1
|
|
64
|
+
signing_key:
|
|
65
|
+
specification_version: 2
|
|
66
|
+
summary: Package for interacting/creating Java Management Extensions
|
|
67
|
+
test_files: []
|
|
68
|
+
|