tobias-jmxjr 0.4

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 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
+