tobias-jmxjr 0.4

Sign up to get free protection for your applications and to get access to all the features.
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.txt
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.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("tobias-jmxjr", JMX::VERSION) do |p|
14
+ p.rubyforge_name = "kenai"
15
+ p.url = "http://kenai.com/projects/jmxjr"
16
+ p.author = "Thomas Enebo & Jay McGaffigan"
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,219 @@
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
+ def method_missing(name, *args)
155
+ puts "Invoking: #{name}, #{args}"
156
+ java_args = java_args(args)
157
+ @server.invoke @object_name, name.to_s, java_args, java_types(java_args)
158
+ end
159
+
160
+ private
161
+
162
+ # Define ruby friendly methods for attributes. For odd attribute names or names
163
+ # that you want to call with the actual attribute name you can call aref/aset
164
+ def define_attributes
165
+ @info.attributes.each do |attr|
166
+ rname = underscore(attr.name)
167
+ self.class.__send__(:define_method, rname) { self[attr.name] } if attr.readable?
168
+ self.class.__send__(:define_method, rname + "=") {|v| self[attr.name] = v } if attr.writable?
169
+ end
170
+ end
171
+
172
+ def define_operations
173
+ @info.operations.each do |op|
174
+ self.class.__send__(:define_method, op.name) do |*args|
175
+ jargs, jtypes = java_args(op.signature, args)
176
+ @server.invoke @object_name, op.name, jargs, jtypes
177
+ end
178
+ end
179
+ end
180
+
181
+ # Given the signature and the parameters supplied do these signatures match.
182
+ # Repackage these parameters as Java objects in a primitive object array.
183
+ def java_args(signature, params)
184
+ return nil if params.nil?
185
+
186
+ jtypes = []
187
+ jargs = []
188
+ params.each_with_index do |param, i|
189
+ type = signature[i].get_type
190
+ jtypes << type
191
+ required_type = JavaClass.for_name(type)
192
+
193
+ java_arg = param.to_java(:object)
194
+
195
+ if (param.kind_of? Array)
196
+ java_arg = param.inject(ArrayList.new) {|l, element| l << element }
197
+ end
198
+
199
+ jargs << java_arg
200
+
201
+ arg_type = java_arg.java_class
202
+
203
+ raise TypeError.new("parameter #{signature[i].name} expected to be #{required_type}, but was #{arg_type}") if !required_type.assignable_from? arg_type
204
+ end
205
+ [jargs.to_java, jtypes.to_java(:string)]
206
+ end
207
+
208
+ # Convert a collection of java objects to their Java class name equivalents
209
+ def java_types(params)
210
+ return nil if params.nil?
211
+
212
+ params.map {|e| e.class.java_class.name }.to_java(:string)
213
+ end
214
+
215
+ def underscore(string)
216
+ string.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,245 @@
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 Thread.current[:op].nil?
113
+ Thread.current[:op].name = name
114
+ operations << Thread.current[:op].to_jmx
115
+ Thread.current[:op] = nil
116
+ end
117
+
118
+ def self.attributes #:nodoc:
119
+ Thread.current[:attrs] ||= []
120
+ end
121
+
122
+ def self.operations #:nodoc:
123
+ Thread.current[:ops] ||= []
124
+ end
125
+
126
+ def self.define_getter(name, type)
127
+ # FIXME: Our to_java_type needs to do something saner
128
+ java_type = begin; to_java_type(type); rescue; nil; end
129
+ value_proc = java_type ? proc { |value| java_type.new value } : proc { |value| Java.ruby_to_java value }
130
+
131
+ define_method("jmx_get_#{name.downcase}") do
132
+ javax.management.Attribute.new name, value_proc.call(instance_variable_get('@' + name))
133
+ end
134
+ end
135
+
136
+ def self.define_setter(name, type)
137
+ value_converter = JMX::JavaTypeAware.to_ruby(type)
138
+
139
+ define_method("jmx_set_#{name.downcase}") do |value|
140
+ instance_variable_set '@' + name, value_converter.call(value)
141
+ end
142
+ end
143
+
144
+ # the <tt>rw_attribute</tt> method is used to declare a JMX read write attribute. See the +JavaSimpleTypes+
145
+ # module for more information about acceptable types usage:
146
+ # rw_attribute :attribute_name, :string, "Description displayed in a JMX console"
147
+ def self.rw_attribute(name, type, description)
148
+ name = name.to_s
149
+ attributes << JMX::Attribute.new(name, type, description, true, true).to_jmx
150
+ attr_accessor name
151
+ define_getter name, type
152
+ define_setter name, type
153
+ end
154
+
155
+ # the <tt>r_attribute</tt> method is used to declare a JMX read only attribute. See the +JavaSimpleTypes+
156
+ # module for more information about acceptable types usage:
157
+ # r_attribute :attribute_name, :string, "Description displayed in a JMX console"
158
+ def self.r_attribute(name, type, description)
159
+ name = name.to_s
160
+ attributes << JMX::Attribute.new(name, type, description, true, false).to_jmx
161
+ attr_reader name
162
+ define_getter name, type
163
+ end
164
+
165
+ # the <tt>w_attribute</tt> method is used to declare a JMX write only attribute. See the +JavaSimpleTypes+
166
+ # module for more information about acceptable types usage:
167
+ # w_attribute :attribute_name, :string, "Description displayed in a JMX console"
168
+ def self.w_attribute(name, type, description)
169
+ name = name.to_s
170
+ attributes << JMX::Attribute.new(name, type, description, false, true).to_jmx
171
+ attr_writer name
172
+ define_setter name, type
173
+ end
174
+
175
+ # Use the operation method to declare the start of an operation
176
+ # It takes as an argument the description for the operation
177
+ # operation "Used to start the service"
178
+ # def start
179
+ # end
180
+ #--
181
+ # Last operation wins if more than one
182
+ #++
183
+ def self.operation(description)
184
+ # Wait to error check until method_added so we can know method name
185
+ Thread.current[:op] = JMX::Operation.new description
186
+ end
187
+
188
+ # Used to declare a parameter (you can declare more than one in succession) that
189
+ # is associated with the currently declared operation.
190
+ # operation "Used to update the name of a service"
191
+ # parameter :string, "name", "Set the new name of the service"
192
+ # def start
193
+ # end
194
+ def self.parameter(type, name=nil, description=nil)
195
+ Thread.current[:op].parameters << JMX::Parameter.new(type, name, description)
196
+ end
197
+
198
+ # Used to declare the return type of the operation
199
+ # operation "Used to update the name of a service"
200
+ # parameter :string, "name", "Set the new name of the service"
201
+ # returns :void
202
+ # def set_name
203
+ # end
204
+ def self.returns(type)
205
+ Thread.current[:op].return_type = type
206
+ end
207
+
208
+ # when creating a dynamic MBean we need to provide it with a name and a description.
209
+ def initialize(name, description)
210
+ operations = self.class.operations.to_java MBeanOperationInfo
211
+ attributes = self.class.attributes.to_java MBeanAttributeInfo
212
+ @info = MBeanInfo.new name, description, attributes, nil, operations, nil
213
+ end
214
+
215
+ # Retrieve the value of the requested attribute (where attribute is a javax.management.Attribute class)
216
+ def getAttribute(attribute)
217
+ __send__("jmx_get_" + attribute.downcase)
218
+ end
219
+
220
+ def getAttributes(attributes)
221
+ attributes.inject(AttributeList.new) { |attrs, attribute| attrs.add(getAttribute(attribute)); attrs }
222
+ end
223
+
224
+ def getMBeanInfo
225
+ @info
226
+ end
227
+
228
+ def invoke(actionName, params=nil, signature=nil)
229
+ __send__(actionName, *params)
230
+ end
231
+
232
+ def setAttribute(attribute)
233
+ __send__("jmx_set_#{attribute.name.downcase}", attribute.value)
234
+ end
235
+
236
+ def setAttributes(attributes)
237
+ attributes.each { |attribute| setAttribute attribute}
238
+ end
239
+
240
+ def toString
241
+ "#@info.class_name: #@info.description"
242
+ end
243
+ alias_method :to_s, :toString
244
+ alias_method :inspect, :toString
245
+ 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
@@ -0,0 +1,3 @@
1
+ module JMX
2
+ VERSION = "0.4"
3
+ end
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,79 @@
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
+ end
24
+
25
+
26
+ class JMXAttributeTest < Test::Unit::TestCase
27
+
28
+ def setup
29
+ @madb = MyAttributeDynamicBean.new("test.MyTestBean","Mwahahahahahah")
30
+ end
31
+
32
+ #make sure we didn't break anything from a ruby perspective
33
+ def test_can_create_bean_and_access_accessor_type_methods
34
+ @madb.set_number_read_only 4
35
+ assert_nil(@madb.name)
36
+ @madb.name = "Name"
37
+ assert_equal("Name", @madb.name)
38
+ assert_equal(4, @madb.number_read_only)
39
+ @madb.number_write_only = 4
40
+ assert_equal(4, @madb.fetch_number_write_only)
41
+ assert_raise(NoMethodError) { @madb.number_write_only }
42
+ end
43
+
44
+ def test_get_attributes_via_dynamicmbeaninterface
45
+ @madb.set_number_read_only 4
46
+ @madb.name = "Name"
47
+
48
+ assert_equal(@madb.name, @madb.getAttribute("name").get_value.to_s)
49
+ assert_equal(@madb.number_read_only, @madb.getAttribute("number_read_only").get_value)
50
+ atts = ["name", "number_read_only"]
51
+ retrieved = @madb.getAttributes(atts)
52
+ assert_equal(2, retrieved.length)
53
+ #TODO: assertion comparing the types in teh array to java types
54
+ end
55
+
56
+ def test_set_attributes_via_dynamicbeaninterface
57
+ @madb.name = "blue"
58
+ red = java.lang.String.new("red")
59
+ attribute = javax.management.Attribute.new("name", red)
60
+ @madb.setAttribute(attribute)
61
+
62
+ assert_equal("String", @madb.name.class.to_s )
63
+ assert_equal("red", @madb.name)
64
+ end
65
+
66
+ def test_set_multiple_attributes_via_dynamicbeaninterface
67
+ @madb.name = "blue"
68
+ three = java.lang.Integer.new(3)
69
+ red = java.lang.String.new("red")
70
+ attribute1 = javax.management.Attribute.new("name", red)
71
+ attribute2 = javax.management.Attribute.new("number_write_only", three)
72
+
73
+ @madb.setAttributes([attribute1, attribute2])
74
+ assert_equal("red", @madb.name)
75
+ assert_equal(3, @madb.fetch_number_write_only)
76
+ end
77
+
78
+ end
79
+
@@ -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,91 @@
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
+ rw_attribute :name, :string, "My sample attribute"
14
+
15
+ operation "Doubles a value"
16
+ parameter :int, "a", "Value to double"
17
+ returns :int
18
+ def double(a)
19
+ a + a
20
+ end
21
+
22
+ operation "Doubles a string"
23
+ parameter :string, "a", "Value to double"
24
+ returns :string
25
+ def string_double(a)
26
+ a + a
27
+ end
28
+
29
+ operation "Give me foo"
30
+ returns :string
31
+ def foo
32
+ "foo"
33
+ end
34
+
35
+ operation "Concatentates a list"
36
+ parameter :list, "list", "List to concatenate"
37
+ returns :string
38
+ def concat(list)
39
+ list.inject("") { |memo, element| memo << element.to_s }
40
+ end
41
+ end
42
+
43
+ class JMXServerTest < Test::Unit::TestCase
44
+ PORT = 9999
45
+ URL = "service:jmx:rmi:///jndi/rmi://localhost:#{PORT}/jmxrmi"
46
+
47
+ def setup
48
+ @registry = RMIRegistry.new PORT
49
+ @server = JMX::MBeanServer.new
50
+ @connector = JMX::MBeanServerConnector.new(URL, @server)
51
+ @connector.start
52
+ @client = JMX::connect(:port => PORT)
53
+ end
54
+
55
+ def teardown
56
+ @connector.stop
57
+ @registry.stop
58
+ end
59
+
60
+ def test_ruby_mbean
61
+ dyna = MyDynamicMBean.new("domain.MySuperBean", "Heh")
62
+ domain = @server.default_domain
63
+ @server.register_mbean dyna, "#{domain}:type=MyDynamicMBean"
64
+
65
+ # Get bean from client connector connection
66
+ bean = @client["#{domain}:type=MyDynamicMBean"]
67
+ assert_equal("foo", bean.foo)
68
+ assert_equal(6, bean.double(3))
69
+ assert_raise(TypeError) { puts bean.double("HEH") }
70
+ assert_equal("hehheh", bean.string_double("heh"))
71
+ assert_equal("123", bean.concat([1,2,3]))
72
+
73
+ assert_nil(bean.name)
74
+ bean.name = "Name"
75
+ assert_equal("Name", bean.name)
76
+ end
77
+
78
+ def test_ruby_mbean_twice
79
+ dyna = MyDynamicMBean.new("domain.MySuperBean", "Heh")
80
+ domain = @server.default_domain
81
+ @server.unregister_mbean "#{domain}:type=MyDynamicMBean"
82
+ @server.register_mbean dyna, "#{domain}:type=MyDynamicMBean"
83
+ # Get bean from client connector connection
84
+ bean = @client["#{domain}:type=MyDynamicMBean"]
85
+ assert_equal("foo", bean.foo)
86
+ assert_equal(6, bean.double(3))
87
+ assert_raise(TypeError) { puts bean.double("HEH") }
88
+ assert_equal("hehheh", bean.string_double("heh"))
89
+ assert_equal("123", bean.concat([1,2,3]))
90
+ end
91
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tobias-jmxjr
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: "0.4"
6
+ platform: ruby
7
+ authors:
8
+ - Thomas Enebo & Jay McGaffigan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-03-07 00:00:00 -05:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: Install this gem and require 'jmx' to load the library.
18
+ email: enebo@acm.org
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - Manifest.txt
25
+ - README.txt
26
+ - LICENSE.txt
27
+ files:
28
+ - Manifest.txt
29
+ - Rakefile
30
+ - README.txt
31
+ - LICENSE.txt
32
+ - lib/jmx.rb
33
+ - lib/rmi.rb
34
+ - lib/jmx/dynamic_mbean.rb
35
+ - lib/jmx/server.rb
36
+ - lib/jmx/version.rb
37
+ - samples/memory.rb
38
+ - test/jmx_attribute_test.rb
39
+ - test/jmx_client_test.rb
40
+ - test/jmx_server_test.rb
41
+ - .gemtest
42
+ has_rdoc: true
43
+ homepage: http://kenai.com/projects/jmxjr
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --main
49
+ - README.txt
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ requirements: []
65
+
66
+ rubyforge_project: kenai
67
+ rubygems_version: 1.6.1
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Package for interacting/creating Java Management Extensions
71
+ test_files: []
72
+