tobias-jmx 0.8
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/.gemtest +0 -0
- data/LICENSE.txt +20 -0
- data/Manifest.txt +14 -0
- data/README.md +29 -0
- data/Rakefile +27 -0
- data/lib/jmx.rb +218 -0
- data/lib/jmx/dynamic_mbean.rb +270 -0
- data/lib/jmx/server.rb +131 -0
- data/lib/jmx/version.rb +3 -0
- data/lib/rmi.rb +21 -0
- data/samples/memory.rb +29 -0
- data/test/jmx_attribute_test.rb +82 -0
- data/test/jmx_client_test.rb +86 -0
- data/test/jmx_server_test.rb +129 -0
- metadata +71 -0
data/.gemtest
ADDED
File without changes
|
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
@@ -0,0 +1,14 @@
|
|
1
|
+
Manifest.txt
|
2
|
+
Rakefile
|
3
|
+
README.md
|
4
|
+
LICENSE.txt
|
5
|
+
lib/jmx
|
6
|
+
lib/jmx.rb
|
7
|
+
lib/rmi.rb
|
8
|
+
lib/jmx/dynamic_mbean.rb
|
9
|
+
lib/jmx/server.rb
|
10
|
+
lib/jmx/version.rb
|
11
|
+
samples/memory.rb
|
12
|
+
test/jmx_attribute_test.rb
|
13
|
+
test/jmx_client_test.rb
|
14
|
+
test/jmx_server_test.rb
|
data/README.md
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
|
+
## FEATURES/PROBLEMS:
|
9
|
+
|
10
|
+
* Use '-J-Dcom.sun.management.jmxremote' to make jruby process accessible from a jruby command-line
|
11
|
+
|
12
|
+
## SYNOPSIS:
|
13
|
+
|
14
|
+
Connect to same JVM as client script and look at Memory MBean
|
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.md", "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("tobias-jmx", JMX::VERSION) do |p|
|
14
|
+
p.rubyforge_name = "jruby-extras"
|
15
|
+
p.url = "http://github.com/enebo/jmxjr"
|
16
|
+
p.author = "Thomas Enebo & Jay McGaffigan"
|
17
|
+
p.email = "tom.enebo@gmail.com"
|
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,218 @@
|
|
1
|
+
include Java
|
2
|
+
|
3
|
+
require 'rmi'
|
4
|
+
require 'jmx/dynamic_mbean'
|
5
|
+
require 'jmx/server'
|
6
|
+
|
7
|
+
java_import java.util.ArrayList
|
8
|
+
java_import javax.management.Attribute
|
9
|
+
java_import javax.management.MBeanInfo
|
10
|
+
java_import javax.management.DynamicMBean
|
11
|
+
|
12
|
+
module JMX
|
13
|
+
java_import javax.management.ObjectName
|
14
|
+
class ObjectName
|
15
|
+
def [](key)
|
16
|
+
get_key_property(key.to_s)
|
17
|
+
end
|
18
|
+
|
19
|
+
def info(server)
|
20
|
+
server.getMBeanInfo(self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module javax::management::openmbean::CompositeData
|
26
|
+
include Enumerable
|
27
|
+
|
28
|
+
def [](key)
|
29
|
+
get(key.to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(name, *args)
|
33
|
+
self[name]
|
34
|
+
end
|
35
|
+
|
36
|
+
def each
|
37
|
+
get_composite_type.key_set.each { |key| yield key }
|
38
|
+
end
|
39
|
+
|
40
|
+
def each_pair
|
41
|
+
get_composite_type.key_set.each { |key| yield key, get(key) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module JMX
|
46
|
+
##
|
47
|
+
# Connect to a MBeanServer
|
48
|
+
# opts can contain several values
|
49
|
+
# :host - The hostname where the server resides (def: localhost)
|
50
|
+
# :port - Which port the server resides at (def: 8686)
|
51
|
+
# :url_path - path part of JMXServerURL (def: /jmxrmi)
|
52
|
+
# :user - User to connect as (optional)
|
53
|
+
# :password - Password for user (optional)
|
54
|
+
def self.connect(opts = {})
|
55
|
+
host = opts[:host] || 'localhost'
|
56
|
+
port = opts[:port] || 8686
|
57
|
+
url_path = opts[:url_path] || "/jmxrmi"
|
58
|
+
url = "service:jmx:rmi:///jndi/rmi://#{host}:#{port}#{url_path}"
|
59
|
+
|
60
|
+
if opts[:user]
|
61
|
+
JMX::MBeanServer.new url, opts[:user], opts[:password]
|
62
|
+
else
|
63
|
+
JMX::MBeanServer.new url
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# sad little simple server setup so you can connect up to it.
|
69
|
+
#
|
70
|
+
def self.simple_server(opts = {})
|
71
|
+
port = opts[:port] || 8686
|
72
|
+
url_path = opts[:url_path] || "/jmxrmi"
|
73
|
+
url = "service:jmx:rmi:///jndi/rmi://localhost:#{port}#{url_path}"
|
74
|
+
$registry = RMIRegistry.new port
|
75
|
+
@connector = JMX::MBeanServerConnector.new(url, JMX::MBeanServer.new).start
|
76
|
+
end
|
77
|
+
|
78
|
+
# Holder for beans created from retrieval (namespace protection [tm]).
|
79
|
+
# This also gives MBeans nicer names when inspected
|
80
|
+
module MBeans
|
81
|
+
##
|
82
|
+
# Create modules in this namespace for each package in the Java fully
|
83
|
+
# qualified name and return the deepest module along with the Java class
|
84
|
+
# name back to the caller.
|
85
|
+
def self.parent_for(java_class_fqn)
|
86
|
+
java_class_fqn.split(".").inject(MBeans) do |parent, segment|
|
87
|
+
# Note: We are boned if java class name is lower cased
|
88
|
+
return [parent, segment] if segment =~ /^[A-Z]/
|
89
|
+
|
90
|
+
segment.capitalize!
|
91
|
+
unless parent.const_defined? segment
|
92
|
+
parent.const_set segment, Module.new
|
93
|
+
else
|
94
|
+
parent.const_get segment
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Create a Ruby proxy based on the MBean represented by the object_name
|
102
|
+
class MBeanProxy
|
103
|
+
# Generate a friendly Ruby proxy for the MBean represented by object_name
|
104
|
+
def self.generate(server, object_name)
|
105
|
+
parent, class_name = MBeans.parent_for object_name.info(server).class_name
|
106
|
+
|
107
|
+
if parent.const_defined? class_name
|
108
|
+
proxy = parent.const_get(class_name)
|
109
|
+
else
|
110
|
+
proxy = Class.new MBeanProxy
|
111
|
+
parent.const_set class_name, proxy
|
112
|
+
end
|
113
|
+
|
114
|
+
proxy.new(server, object_name)
|
115
|
+
end
|
116
|
+
|
117
|
+
def initialize(server, object_name)
|
118
|
+
@server, @object_name = server, object_name
|
119
|
+
@info = @server.getMBeanInfo(@object_name)
|
120
|
+
|
121
|
+
define_attributes
|
122
|
+
define_operations
|
123
|
+
end
|
124
|
+
|
125
|
+
def attributes
|
126
|
+
@attributes ||= @info.attributes.inject([]) { |s,attr| s << attr.name }
|
127
|
+
end
|
128
|
+
|
129
|
+
def operations
|
130
|
+
@operations ||= @info.operations.inject([]) { |s,op| s << op.name }
|
131
|
+
end
|
132
|
+
|
133
|
+
# Get MBean attribute specified by name. If it is just a plain attribute then
|
134
|
+
# unwrap the attribute and just return the value.
|
135
|
+
def [](name)
|
136
|
+
attribute = @server.getAttribute(@object_name, name.to_s)
|
137
|
+
return attribute.value if attribute.kind_of? javax.management.Attribute
|
138
|
+
attribute
|
139
|
+
end
|
140
|
+
|
141
|
+
# Set MBean attribute specified by name to value
|
142
|
+
def []=(name, value)
|
143
|
+
@server.setAttribute @object_name, javax.management.Attribute.new(name.to_s, value)
|
144
|
+
end
|
145
|
+
|
146
|
+
def add_notification_listener(filter=nil, handback=nil, &listener)
|
147
|
+
@server.addNotificationListener @object_name, listener, filter, handback
|
148
|
+
end
|
149
|
+
|
150
|
+
def remove_notification_listener(listener)
|
151
|
+
@server.removeNotificationListener @object_name, listener
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def safe_define_method(method_name, &block)
|
157
|
+
method_name = "mbean_#{method_name}" if respond_to?(method_name) || method_name == 'initialize'
|
158
|
+
self.class.__send__(:define_method, method_name, &block)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Define ruby friendly methods for attributes. For odd attribute names or names
|
162
|
+
# that you want to call with the actual attribute name you can call aref/aset
|
163
|
+
def define_attributes
|
164
|
+
@info.attributes.each do |attr|
|
165
|
+
rname = underscore(attr.name)
|
166
|
+
safe_define_method(rname) { self[attr.name] } if attr.readable?
|
167
|
+
safe_define_method(rname + "=") {|v| self[attr.name] = v } if attr.writable?
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def define_operations
|
172
|
+
@info.operations.each do |op|
|
173
|
+
safe_define_method(op.name) do |*args|
|
174
|
+
jargs, jtypes = java_args(op.signature, args)
|
175
|
+
@server.invoke @object_name, op.name, jargs, jtypes
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Given the signature and the parameters supplied do these signatures match.
|
181
|
+
# Repackage these parameters as Java objects in a primitive object array.
|
182
|
+
def java_args(signature, params)
|
183
|
+
return nil if params.nil?
|
184
|
+
|
185
|
+
jtypes = []
|
186
|
+
jargs = []
|
187
|
+
params.each_with_index do |param, i|
|
188
|
+
type = signature[i].get_type
|
189
|
+
jtypes << type
|
190
|
+
required_type = JavaClass.for_name(type)
|
191
|
+
|
192
|
+
java_arg = param.to_java(:object)
|
193
|
+
|
194
|
+
if (param.kind_of? Array)
|
195
|
+
java_arg = param.inject(ArrayList.new) {|l, element| l << element }
|
196
|
+
end
|
197
|
+
|
198
|
+
jargs << java_arg
|
199
|
+
|
200
|
+
arg_type = java_arg.java_class
|
201
|
+
|
202
|
+
raise TypeError.new("parameter #{signature[i].name} expected to be #{required_type}, but was #{arg_type}") if !required_type.assignable_from? arg_type
|
203
|
+
end
|
204
|
+
[jargs.to_java, jtypes.to_java(:string)]
|
205
|
+
end
|
206
|
+
|
207
|
+
# Convert a collection of java objects to their Java class name equivalents
|
208
|
+
def java_types(params)
|
209
|
+
return nil if params.nil?
|
210
|
+
|
211
|
+
params.map {|e| e.class.java_class.name }.to_java(:string)
|
212
|
+
end
|
213
|
+
|
214
|
+
def underscore(string)
|
215
|
+
string.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
module JMX
|
2
|
+
java_import javax.management.MBeanParameterInfo
|
3
|
+
java_import javax.management.MBeanOperationInfo
|
4
|
+
java_import javax.management.MBeanAttributeInfo
|
5
|
+
java_import javax.management.MBeanInfo
|
6
|
+
|
7
|
+
# Module that is used to bridge java to ruby and ruby to java types.
|
8
|
+
module JavaTypeAware
|
9
|
+
# Current list of types we understand If it's not in this list we are
|
10
|
+
# assuming that we are going to convert to a java.object
|
11
|
+
SIMPLE_TYPES = {
|
12
|
+
:int => ['java.lang.Integer', lambda {|param| param.to_i}],
|
13
|
+
:list => ['java.util.List', lambda {|param| param.to_a}],
|
14
|
+
:long => ['java.lang.Long', lambda {|param| param.to_i}],
|
15
|
+
:float => ['java.lang.Float', lambda {|param| param.to_f}],
|
16
|
+
:map => ['java.util.Map', lambda {|param| param}],
|
17
|
+
:set => ['java.util.Set', lambda {|param| param}],
|
18
|
+
:string => ['java.lang.String', lambda {|param| param.to_s}],
|
19
|
+
:void => ['java.lang.Void', lambda {|param| nil}]
|
20
|
+
}
|
21
|
+
|
22
|
+
def to_java_type(type_name)
|
23
|
+
SIMPLE_TYPES[type_name][0] || type_name
|
24
|
+
end
|
25
|
+
|
26
|
+
#TODO: I'm not sure this is strictly needed, but funky things can happen if you
|
27
|
+
# are expecting your attributes (from the ruby side) to be ruby types and they are java types.
|
28
|
+
def to_ruby(type_name)
|
29
|
+
SIMPLE_TYPES[type_name][1] || lambda {|param| param}
|
30
|
+
end
|
31
|
+
|
32
|
+
module_function :to_java_type, :to_ruby
|
33
|
+
end
|
34
|
+
|
35
|
+
class Parameter
|
36
|
+
include JavaTypeAware
|
37
|
+
|
38
|
+
def initialize(type, name, description)
|
39
|
+
@type, @name, @description = type, name, description
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_jmx
|
43
|
+
MBeanParameterInfo.new @name.to_s, to_java_type(@type), @description
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Operation < Struct.new(:description, :parameters, :return_type, :name, :impact)
|
48
|
+
include JavaTypeAware
|
49
|
+
|
50
|
+
def initialize(description)
|
51
|
+
super
|
52
|
+
self.parameters, self.impact, self.description = [], MBeanOperationInfo::UNKNOWN, description
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_jmx
|
56
|
+
java_parameters = parameters.map { |parameter| parameter.to_jmx }
|
57
|
+
MBeanOperationInfo.new name.to_s, description, java_parameters.to_java(javax.management.MBeanParameterInfo), to_java_type(return_type), impact
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Attribute < Struct.new(:name, :type, :description, :is_reader, :is_writer, :is_iser)
|
62
|
+
include JavaTypeAware
|
63
|
+
|
64
|
+
def initialize(name, type, description, is_rdr, is_wrtr)
|
65
|
+
super
|
66
|
+
self.description, self.type, self.name = description, type, name
|
67
|
+
self.is_reader,self.is_writer, self.is_iser = is_rdr, is_wrtr, false
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_jmx
|
71
|
+
MBeanAttributeInfo.new(name.to_s, to_java_type(type), description, is_reader, is_writer, is_iser)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# The Ruby-Java JMX utilities work throughout the DynamicMBean concept. Creators of Ruby based MBeans must inherit this
|
77
|
+
# class (<tt>RubyDynamicMBean</tt>) in their own bean classes and then register them with a JMX mbean server.
|
78
|
+
# Here is an example:
|
79
|
+
# class MyMBean < RuybDynamicMBean
|
80
|
+
# rw_attribute :status, :string, "Status information for this process"
|
81
|
+
#
|
82
|
+
# operation "Shutdown this process"
|
83
|
+
# parameter :string, "user_name", "Name of user requesting shutdown"
|
84
|
+
# returns :string
|
85
|
+
# def shutdown(user_name)
|
86
|
+
# "shutdown requests more time"
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
# Once you have defined your bean class you can start declaring attributes and operations.
|
90
|
+
# Attributes come in three flavors: read, write, and read write. Simmilar to the <tt>attr*</tt>
|
91
|
+
# helpers, there are helpers that are used to create management attributes. Use +r_attribute+,
|
92
|
+
# +w_attribute+, and +rw_attribute+ to declare attributes, and the +operation+, +returns+,
|
93
|
+
# and +parameter+ helpers to define a management operation.
|
94
|
+
# Creating attributes with the *_attribute convention ALSO creates ruby accessors
|
95
|
+
# (it invokes the attr_accessor/attr_reader/attr_writer ruby helpers) to create ruby methods
|
96
|
+
# like: user_name= and username. So in your ruby code you can treat the attributes
|
97
|
+
# as "regular" ruby accessors
|
98
|
+
class RubyDynamicMBean
|
99
|
+
java_import javax.management.AttributeList
|
100
|
+
java_import javax.management.MBeanOperationInfo
|
101
|
+
java_import javax.management.MBeanAttributeInfo
|
102
|
+
java_import javax.management.MBeanInfo
|
103
|
+
include JMX::JavaTypeAware
|
104
|
+
|
105
|
+
def self.inherited(cls)
|
106
|
+
cls.send(:include, javax.management.DynamicMBean)
|
107
|
+
end
|
108
|
+
|
109
|
+
# TODO: preserve any original method_added?
|
110
|
+
# TODO: Error handling here when it all goes wrong?
|
111
|
+
def self.method_added(name) #:nodoc:
|
112
|
+
return if metadata[:op].nil?
|
113
|
+
metadata[:op].name = name
|
114
|
+
operations << metadata[:op].to_jmx
|
115
|
+
metadata[:op] = nil
|
116
|
+
end
|
117
|
+
|
118
|
+
# All attributes in this class
|
119
|
+
def self.attributes
|
120
|
+
metadata[:attrs] ||= []
|
121
|
+
end
|
122
|
+
|
123
|
+
# All attributes up the inheritance chain
|
124
|
+
def self.all_attributes
|
125
|
+
ancestors.inject([]) do |sum, clazz|
|
126
|
+
sum.concat(clazz.attributes) if clazz.respond_to? :attributes
|
127
|
+
sum
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# All operations up the inheritance chain
|
132
|
+
def self.all_operations
|
133
|
+
ancestors.inject([]) do |sum, clazz|
|
134
|
+
sum.concat(clazz.operations) if clazz.respond_to? :operations
|
135
|
+
sum
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# All operations in this class
|
140
|
+
def self.operations
|
141
|
+
metadata[:ops] ||= []
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.define_getter(name, type, reader)
|
145
|
+
# FIXME: Our to_java_type needs to do something saner
|
146
|
+
java_type = begin; to_java_type(type); rescue; nil; end
|
147
|
+
value_proc = java_type ? proc { |value| java_type.new value } : proc { |value| Java.ruby_to_java value }
|
148
|
+
|
149
|
+
reader = name.to_s unless reader
|
150
|
+
attr_reader reader unless instance_methods.include?(reader)
|
151
|
+
define_method("jmx_get_#{name.downcase}") do
|
152
|
+
javax.management.Attribute.new name, value_proc.call(__send__(reader))
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.define_setter(name, type, writer)
|
157
|
+
value_converter = JMX::JavaTypeAware.to_ruby(type)
|
158
|
+
|
159
|
+
writer = name.to_s + '=' unless writer
|
160
|
+
attr_writer name.to_s unless instance_methods.include?(writer)
|
161
|
+
define_method("jmx_set_#{name.downcase}") do |value|
|
162
|
+
__send__ writer, value_converter.call(value)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# the <tt>rw_attribute</tt> method is used to declare a JMX read write attribute. See the +JavaSimpleTypes+
|
167
|
+
# module for more information about acceptable types usage:
|
168
|
+
# rw_attribute :attribute_name, :string, "Description displayed in a JMX console"
|
169
|
+
def self.rw_attribute(name, type, description, reader=nil, writer=nil)
|
170
|
+
name = name.to_s
|
171
|
+
attributes << JMX::Attribute.new(name, type, description, true, true).to_jmx
|
172
|
+
define_getter name, type, reader
|
173
|
+
define_setter name, type, writer
|
174
|
+
end
|
175
|
+
|
176
|
+
# the <tt>r_attribute</tt> method is used to declare a JMX read only attribute. See the +JavaSimpleTypes+
|
177
|
+
# module for more information about acceptable types usage:
|
178
|
+
# r_attribute :attribute_name, :string, "Description displayed in a JMX console"
|
179
|
+
def self.r_attribute(name, type, description, reader=nil)
|
180
|
+
name = name.to_s
|
181
|
+
attributes << JMX::Attribute.new(name, type, description, true, false).to_jmx
|
182
|
+
define_getter name, type, reader
|
183
|
+
end
|
184
|
+
|
185
|
+
# the <tt>w_attribute</tt> method is used to declare a JMX write only attribute. See the +JavaSimpleTypes+
|
186
|
+
# module for more information about acceptable types usage:
|
187
|
+
# w_attribute :attribute_name, :string, "Description displayed in a JMX console"
|
188
|
+
def self.w_attribute(name, type, description, writer=nil)
|
189
|
+
name = name.to_s
|
190
|
+
attributes << JMX::Attribute.new(name, type, description, false, true).to_jmx
|
191
|
+
define_setter name, type, writer
|
192
|
+
end
|
193
|
+
|
194
|
+
# Use the operation method to declare the start of an operation
|
195
|
+
# It takes as an argument the description for the operation
|
196
|
+
# operation "Used to start the service"
|
197
|
+
# def start
|
198
|
+
# end
|
199
|
+
#--
|
200
|
+
# Last operation wins if more than one
|
201
|
+
#++
|
202
|
+
def self.operation(description)
|
203
|
+
# Wait to error check until method_added so we can know method name
|
204
|
+
metadata[:op] = JMX::Operation.new description
|
205
|
+
end
|
206
|
+
|
207
|
+
# Used to declare a parameter (you can declare more than one in succession) that
|
208
|
+
# is associated with the currently declared operation.
|
209
|
+
# operation "Used to update the name of a service"
|
210
|
+
# parameter :string, "name", "Set the new name of the service"
|
211
|
+
# def start
|
212
|
+
# end
|
213
|
+
def self.parameter(type, name=nil, description=nil)
|
214
|
+
metadata[:op].parameters << JMX::Parameter.new(type, name, description)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Used to declare the return type of the operation
|
218
|
+
# operation "Used to update the name of a service"
|
219
|
+
# parameter :string, "name", "Set the new name of the service"
|
220
|
+
# returns :void
|
221
|
+
# def set_name
|
222
|
+
# end
|
223
|
+
def self.returns(type)
|
224
|
+
metadata[:op].return_type = type
|
225
|
+
end
|
226
|
+
|
227
|
+
# Thread local storage for the derived bean
|
228
|
+
def self.metadata
|
229
|
+
@@metadata ||= {}
|
230
|
+
@@metadata[object_id] ||= {}
|
231
|
+
end
|
232
|
+
|
233
|
+
# when creating a dynamic MBean we need to provide it with a name and a description.
|
234
|
+
def initialize(name, description)
|
235
|
+
operations = self.class.all_operations.to_java MBeanOperationInfo
|
236
|
+
attributes = self.class.all_attributes.to_java MBeanAttributeInfo
|
237
|
+
@info = MBeanInfo.new name, description, attributes, nil, operations, nil
|
238
|
+
end
|
239
|
+
|
240
|
+
# Retrieve the value of the requested attribute (where attribute is a javax.management.Attribute class)
|
241
|
+
def getAttribute(attribute)
|
242
|
+
__send__("jmx_get_" + attribute.downcase)
|
243
|
+
end
|
244
|
+
|
245
|
+
def getAttributes(attributes)
|
246
|
+
attributes.inject(AttributeList.new) { |attrs, attribute| attrs.add(getAttribute(attribute)); attrs }
|
247
|
+
end
|
248
|
+
|
249
|
+
def getMBeanInfo
|
250
|
+
@info
|
251
|
+
end
|
252
|
+
|
253
|
+
def invoke(actionName, params=nil, signature=nil)
|
254
|
+
__send__(actionName, *params)
|
255
|
+
end
|
256
|
+
|
257
|
+
def setAttribute(attribute)
|
258
|
+
__send__("jmx_set_#{attribute.name.downcase}", attribute.value)
|
259
|
+
end
|
260
|
+
|
261
|
+
def setAttributes(attributes)
|
262
|
+
attributes.each { |attribute| setAttribute attribute}
|
263
|
+
end
|
264
|
+
|
265
|
+
def toString
|
266
|
+
"#@info.class_name: #@info.description"
|
267
|
+
end
|
268
|
+
alias_method :to_s, :toString
|
269
|
+
alias_method :inspect, :toString
|
270
|
+
end
|
data/lib/jmx/server.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
module JMX
|
2
|
+
# The MBeanServer represents a connection to an MBean server
|
3
|
+
# rather than an actual MBean server. Depending upon how
|
4
|
+
# this object is constructed you can either talk to the
|
5
|
+
# PlatformMBeanServer or any "remote" MBean server.
|
6
|
+
#--
|
7
|
+
# Represents both MBeanServer and MBeanServerConnection
|
8
|
+
#++
|
9
|
+
class MBeanServer
|
10
|
+
java_import javax.management.Attribute
|
11
|
+
java_import javax.management.MBeanServerFactory
|
12
|
+
java_import javax.management.remote.JMXConnectorFactory
|
13
|
+
java_import javax.management.remote.JMXServiceURL
|
14
|
+
|
15
|
+
attr_accessor :server
|
16
|
+
@@classes = {}
|
17
|
+
|
18
|
+
# when creatinga new MBeanServer you can optionally specify a location, username, and password
|
19
|
+
# if specify these values (or at least the location) the MBeanServer instance will connect to
|
20
|
+
# an existing (and remote ) MBean server and register the mbeans there.
|
21
|
+
# otherwise the server will connect to to the local Platform MBean Server.
|
22
|
+
def initialize(location=nil, username=nil, password=nil)
|
23
|
+
if (location)
|
24
|
+
env = username ?
|
25
|
+
{"jmx.remote.credentials" => [username, password].to_java(:string)} :
|
26
|
+
nil
|
27
|
+
url = JMXServiceURL.new location
|
28
|
+
@server = JMXConnectorFactory.connect(url, env).getMBeanServerConnection
|
29
|
+
else
|
30
|
+
@server = java.lang.management.ManagementFactory.getPlatformMBeanServer
|
31
|
+
#@server = MBeanServerFactory.createMBeanServer
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](object_name)
|
36
|
+
name = make_object_name object_name
|
37
|
+
|
38
|
+
unless @server.isRegistered(name)
|
39
|
+
raise NoSuchBeanError.new("No name: #{object_name}")
|
40
|
+
end
|
41
|
+
|
42
|
+
#### TODO: Why?
|
43
|
+
@server.getObjectInstance name
|
44
|
+
MBeanProxy.generate(@server, name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def []=(class_name, object_name)
|
48
|
+
name = make_object_name object_name
|
49
|
+
|
50
|
+
@server.createMBean class_name, name, nil, nil
|
51
|
+
|
52
|
+
MBeanProxy.generate(@server, name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def default_domain
|
56
|
+
@server.getDefaultDomain
|
57
|
+
end
|
58
|
+
|
59
|
+
def domains
|
60
|
+
@server.domains
|
61
|
+
end
|
62
|
+
|
63
|
+
def mbean_count
|
64
|
+
@server.getMBeanCount
|
65
|
+
end
|
66
|
+
|
67
|
+
def query_names(name=nil, query=nil)
|
68
|
+
object_name = name.nil? ? nil : make_object_name(name)
|
69
|
+
|
70
|
+
@server.query_names(object_name, query)
|
71
|
+
end
|
72
|
+
|
73
|
+
def unregister_mbean(object_name)
|
74
|
+
name = make_object_name object_name
|
75
|
+
@server.unregisterMBean(name)
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
def register_mbean(object, object_name)
|
80
|
+
name = make_object_name object_name
|
81
|
+
@server.registerMBean(object, name)
|
82
|
+
MBeanProxy.generate(@server, name)
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.find(agent_id=nil)
|
86
|
+
MBeanServerFactory.findMBeanServer(agent_id)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def make_object_name(object_name)
|
92
|
+
return object_name if object_name.kind_of? ObjectName
|
93
|
+
|
94
|
+
ObjectName.new object_name
|
95
|
+
rescue
|
96
|
+
raise ArgumentError.new("Invalid ObjectName #{$!.message}")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class NoSuchBeanError < RuntimeError
|
101
|
+
end
|
102
|
+
|
103
|
+
class MBeanServerConnector
|
104
|
+
java_import javax.management.remote.JMXServiceURL
|
105
|
+
java_import javax.management.remote.JMXConnectorServerFactory
|
106
|
+
|
107
|
+
def initialize(location, server)
|
108
|
+
@url = JMXServiceURL.new location
|
109
|
+
@server = JMXConnectorServerFactory.newJMXConnectorServer @url, nil, server.server
|
110
|
+
|
111
|
+
if block_given?
|
112
|
+
start
|
113
|
+
yield
|
114
|
+
stop
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def active?
|
119
|
+
@server.isActive
|
120
|
+
end
|
121
|
+
|
122
|
+
def start
|
123
|
+
@server.start
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
def stop
|
128
|
+
@server.stop if active?
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/jmx/version.rb
ADDED
data/lib/rmi.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
include Java
|
2
|
+
|
3
|
+
java_import java.rmi.registry.LocateRegistry
|
4
|
+
java_import java.rmi.registry.Registry
|
5
|
+
java_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,82 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'jmx'
|
5
|
+
|
6
|
+
# These tests are for verifying that at a Ruby-level (on server-side) it is still possible
|
7
|
+
# to interact with dynamic mbeans as you expect. *_attributes are backed by ordinary
|
8
|
+
# Ruby instance variables of the same name.
|
9
|
+
|
10
|
+
class MyAttributeDynamicBean < RubyDynamicMBean
|
11
|
+
rw_attribute :name, :string, "My sample attribute"
|
12
|
+
r_attribute :number_read_only, :int, "My sample integer based attribute that is read only"
|
13
|
+
w_attribute :number_write_only, :int, "My sample integer based attribute that is write only"
|
14
|
+
|
15
|
+
# Give us a way to change the attribute for testing
|
16
|
+
def set_number_read_only(value)
|
17
|
+
@number_read_only = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch_number_write_only
|
21
|
+
@number_write_only
|
22
|
+
end
|
23
|
+
|
24
|
+
def my_reader
|
25
|
+
42
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
class JMXAttributeTest < Test::Unit::TestCase
|
31
|
+
|
32
|
+
def setup
|
33
|
+
@madb = MyAttributeDynamicBean.new("test.MyTestBean","Mwahahahahahah")
|
34
|
+
end
|
35
|
+
|
36
|
+
#make sure we didn't break anything from a ruby perspective
|
37
|
+
def test_can_create_bean_and_access_accessor_type_methods
|
38
|
+
@madb.set_number_read_only 4
|
39
|
+
assert_nil(@madb.name)
|
40
|
+
@madb.name = "Name"
|
41
|
+
assert_equal("Name", @madb.name)
|
42
|
+
assert_equal(4, @madb.number_read_only)
|
43
|
+
@madb.number_write_only = 4
|
44
|
+
assert_equal(4, @madb.fetch_number_write_only)
|
45
|
+
assert_raise(NoMethodError) { @madb.number_write_only }
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_get_attributes_via_dynamicmbeaninterface
|
49
|
+
@madb.set_number_read_only 4
|
50
|
+
@madb.name = "Name"
|
51
|
+
|
52
|
+
assert_equal(@madb.name, @madb.getAttribute("name").get_value.to_s)
|
53
|
+
assert_equal(@madb.number_read_only, @madb.getAttribute("number_read_only").get_value)
|
54
|
+
atts = ["name", "number_read_only"]
|
55
|
+
retrieved = @madb.getAttributes(atts)
|
56
|
+
assert_equal(2, retrieved.length)
|
57
|
+
#TODO: assertion comparing the types in teh array to java types
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_set_attributes_via_dynamicbeaninterface
|
61
|
+
@madb.name = "blue"
|
62
|
+
red = java.lang.String.new("red")
|
63
|
+
attribute = javax.management.Attribute.new("name", red)
|
64
|
+
@madb.setAttribute(attribute)
|
65
|
+
|
66
|
+
assert_equal("String", @madb.name.class.to_s )
|
67
|
+
assert_equal("red", @madb.name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_set_multiple_attributes_via_dynamicbeaninterface
|
71
|
+
@madb.name = "blue"
|
72
|
+
three = java.lang.Integer.new(3)
|
73
|
+
red = java.lang.String.new("red")
|
74
|
+
attribute1 = javax.management.Attribute.new("name", red)
|
75
|
+
attribute2 = javax.management.Attribute.new("number_write_only", three)
|
76
|
+
|
77
|
+
@madb.setAttributes([attribute1, attribute2])
|
78
|
+
assert_equal("red", @madb.name)
|
79
|
+
assert_equal(3, @madb.fetch_number_write_only)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'rmi'
|
5
|
+
require 'jmx'
|
6
|
+
|
7
|
+
PORT = 9999
|
8
|
+
$registry = RMIRegistry.new PORT
|
9
|
+
|
10
|
+
class JMXConnectorClientTest < Test::Unit::TestCase
|
11
|
+
URL = "service:jmx:rmi:///jndi/rmi://localhost:#{PORT}/jmxrmi"
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@connector = JMX::MBeanServerConnector.new(URL, JMX::MBeanServer.new)
|
15
|
+
@connector.start
|
16
|
+
@client = JMX::connect(:port => PORT)
|
17
|
+
end
|
18
|
+
|
19
|
+
def teardown
|
20
|
+
@connector.stop
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_invalid_mbean_name
|
24
|
+
assert_raises(ArgumentError) { @client["::::::"] }
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_get_mbean
|
28
|
+
memory = @client["java.lang:type=Memory"]
|
29
|
+
|
30
|
+
assert_not_nil memory, "Could not acquire memory mbean"
|
31
|
+
|
32
|
+
# Attr form
|
33
|
+
heap = memory[:HeapMemoryUsage]
|
34
|
+
assert_not_nil heap
|
35
|
+
assert(heap[:used] > 0, "No heap used? Impossible!")
|
36
|
+
|
37
|
+
# underscored form
|
38
|
+
heap = memory.heap_memory_usage
|
39
|
+
assert_not_nil heap
|
40
|
+
assert(heap.used > 0, "No heap used? Impossible!")
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_set_mbean
|
44
|
+
memory = @client["java.lang:type=Memory"]
|
45
|
+
original_verbose = memory.verbose
|
46
|
+
memory.verbose = !original_verbose
|
47
|
+
assert(memory.verbose != original_verbose, "Could not change verbose")
|
48
|
+
|
49
|
+
memory[:Verbose] = original_verbose
|
50
|
+
assert(memory[:Verbose] == original_verbose, "Could not change back verbose")
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_attributes
|
54
|
+
memory = @client["java.lang:type=Memory"]
|
55
|
+
assert(memory.attributes.include?("HeapMemoryUsage"), "HeapMemoryUsage not found")
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_operations
|
59
|
+
memory = @client["java.lang:type=Memory"]
|
60
|
+
assert(memory.operations.include?("gc"), "gc not found")
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def test_simple_operation
|
65
|
+
memory = @client["java.lang:type=Memory"]
|
66
|
+
|
67
|
+
heap1 = memory[:HeapMemoryUsage][:used]
|
68
|
+
memory.gc
|
69
|
+
heap2 = memory[:HeapMemoryUsage][:used]
|
70
|
+
|
71
|
+
assert(heap1.to_i >= heap2.to_i, "GC did not collect")
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_query_names
|
75
|
+
names = @client.query_names("java.lang:type=MemoryPool,*")
|
76
|
+
assert(names.size > 0, "No memory pools. Impossible!")
|
77
|
+
|
78
|
+
a_memory_pool_bean = @client[names.to_array[0]]
|
79
|
+
assert_not_nil a_memory_pool_bean, "Name must resolve to something"
|
80
|
+
|
81
|
+
usage = a_memory_pool_bean[:Usage]
|
82
|
+
assert_not_nil usage, "Memory pools have usage"
|
83
|
+
|
84
|
+
assert_not_nil usage[:used], "Some memory is used"
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
require 'rmi'
|
6
|
+
require 'jmx'
|
7
|
+
|
8
|
+
class MyDynamicMBean < RubyDynamicMBean
|
9
|
+
rw_attribute :name, :string, "My sample attribute"
|
10
|
+
r_attribute :explicit_reader, :int, "Sample int with writer", :my_reader
|
11
|
+
w_attribute :explicit_writer, :int, "Sample int with writer", :my_writer
|
12
|
+
rw_attribute :explicit_both, :int, "Sample int with writer", :my_read, :my_write
|
13
|
+
|
14
|
+
operation "Doubles a value"
|
15
|
+
parameter :int, "a", "Value to double"
|
16
|
+
returns :int
|
17
|
+
def double(a)
|
18
|
+
a + a
|
19
|
+
end
|
20
|
+
|
21
|
+
operation "Doubles a string"
|
22
|
+
parameter :string, "a", "Value to double"
|
23
|
+
returns :string
|
24
|
+
def string_double(a)
|
25
|
+
a + a
|
26
|
+
end
|
27
|
+
|
28
|
+
operation "Give me foo"
|
29
|
+
returns :string
|
30
|
+
def foo
|
31
|
+
"foo"
|
32
|
+
end
|
33
|
+
|
34
|
+
operation "Concatentates a list"
|
35
|
+
parameter :list, "list", "List to concatenate"
|
36
|
+
returns :string
|
37
|
+
def concat(list)
|
38
|
+
list.inject("") { |memo, element| memo << element.to_s }
|
39
|
+
end
|
40
|
+
|
41
|
+
def my_reader
|
42
|
+
42
|
43
|
+
end
|
44
|
+
|
45
|
+
def my_writer(value)
|
46
|
+
@name = value.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def my_read
|
50
|
+
@frogger
|
51
|
+
end
|
52
|
+
|
53
|
+
def my_write(value)
|
54
|
+
@frogger = value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class MyExtendedDynamicMBean < MyDynamicMBean
|
59
|
+
operation "Triples a value"
|
60
|
+
parameter :int, "a", "Value to triple"
|
61
|
+
returns :int
|
62
|
+
def triple(a)
|
63
|
+
a + a + a
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class JMXServerTest < Test::Unit::TestCase
|
68
|
+
PORT = 9999
|
69
|
+
URL = "service:jmx:rmi:///jndi/rmi://localhost:#{PORT}/jmxrmi"
|
70
|
+
|
71
|
+
def setup
|
72
|
+
@registry = RMIRegistry.new PORT
|
73
|
+
@server = JMX::MBeanServer.new
|
74
|
+
@connector = JMX::MBeanServerConnector.new(URL, @server)
|
75
|
+
@connector.start
|
76
|
+
@client = JMX::connect(:port => PORT)
|
77
|
+
reg_mbean
|
78
|
+
end
|
79
|
+
|
80
|
+
def reg_mbean
|
81
|
+
dyna = MyDynamicMBean.new("domain.MySuperBean", "Heh")
|
82
|
+
@domain = @server.default_domain
|
83
|
+
@server.register_mbean dyna, "#{@domain}:type=MyDynamicMBean"
|
84
|
+
@bean = @client["#{@domain}:type=MyDynamicMBean"]
|
85
|
+
end
|
86
|
+
|
87
|
+
def unreg_mbean
|
88
|
+
@server.unregister_mbean "#{@domain}:type=MyDynamicMBean"
|
89
|
+
end
|
90
|
+
|
91
|
+
def teardown
|
92
|
+
@connector.stop
|
93
|
+
@registry.stop
|
94
|
+
unreg_mbean
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def test_ruby_mbean
|
99
|
+
assert_equal("foo", @bean.foo)
|
100
|
+
assert_equal(6, @bean.double(3))
|
101
|
+
assert_raise(TypeError) { puts @bean.double("HEH") }
|
102
|
+
assert_equal("hehheh", @bean.string_double("heh"))
|
103
|
+
assert_equal("123", @bean.concat([1,2,3]))
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_ruby_mbean_attribtues
|
107
|
+
assert_nil(@bean.name)
|
108
|
+
@bean.name = "Name"
|
109
|
+
assert_equal("Name", @bean.name)
|
110
|
+
|
111
|
+
assert_equal(42, @bean.explicit_reader)
|
112
|
+
@bean.explicit_writer = 69
|
113
|
+
# explicit_writer changes attribute name as a side-effect
|
114
|
+
assert_equal("69", @bean.name)
|
115
|
+
|
116
|
+
@bean.explicit_both = 1
|
117
|
+
assert_equal(1, @bean.explicit_both)
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_extended_mbean
|
121
|
+
dyna = MyExtendedDynamicMBean.new("domain.MySuperBean", "Heh")
|
122
|
+
@server.register_mbean dyna, "#{@domain}:type=MyExtendedDynamicMBean"
|
123
|
+
@bean = @client["#{@domain}:type=MyExtendedDynamicMBean"]
|
124
|
+
|
125
|
+
assert_equal(12, @bean.triple(4))
|
126
|
+
|
127
|
+
@server.unregister_mbean "#{@domain}:type=MyExtendedDynamicMBean"
|
128
|
+
end
|
129
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tobias-jmx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: "0.8"
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Thomas Enebo & Jay McGaffigan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-04-29 00:00:00 -04:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: Install this gem and require 'jmx' to load the library.
|
18
|
+
email: tom.enebo@gmail.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- Manifest.txt
|
25
|
+
- LICENSE.txt
|
26
|
+
files:
|
27
|
+
- Manifest.txt
|
28
|
+
- Rakefile
|
29
|
+
- README.md
|
30
|
+
- LICENSE.txt
|
31
|
+
- lib/jmx.rb
|
32
|
+
- lib/rmi.rb
|
33
|
+
- lib/jmx/dynamic_mbean.rb
|
34
|
+
- lib/jmx/server.rb
|
35
|
+
- lib/jmx/version.rb
|
36
|
+
- samples/memory.rb
|
37
|
+
- test/jmx_attribute_test.rb
|
38
|
+
- test/jmx_client_test.rb
|
39
|
+
- test/jmx_server_test.rb
|
40
|
+
- .gemtest
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/enebo/jmxjr
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --main
|
48
|
+
- README.txt
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project: jruby-extras
|
66
|
+
rubygems_version: 1.5.1
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Package for interacting/creating Java Management Extensions
|
70
|
+
test_files: []
|
71
|
+
|