weblogic-jmx4r 0.1.9

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.
@@ -0,0 +1,16 @@
1
+
2
+ module JMX
3
+ module JDKHelper
4
+ module JDK4
5
+
6
+ class << self
7
+ def method_missing(method, *args, &block)
8
+ raise "JDK (>= 5.0) implementation is not available - \
9
+ maybe only JREs or older JDKs are installed properly."
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,34 @@
1
+
2
+ module JMX
3
+ module JDKHelper
4
+ module JDK5
5
+ java_import sun.jvmstat.monitor.HostIdentifier
6
+ java_import sun.jvmstat.monitor.MonitoredHost
7
+ java_import sun.jvmstat.monitor.MonitoredVmUtil
8
+ java_import sun.jvmstat.monitor.VmIdentifier
9
+ java_import sun.management.ConnectorAddressLink
10
+
11
+ class << self
12
+
13
+ def find_local_url(command_pattern)
14
+ host_id = HostIdentifier.new(nil)
15
+ host = MonitoredHost.get_monitored_host(host_id)
16
+
17
+ host.active_vms.each do |vmid_int|
18
+ vmid = VmIdentifier.new(vmid_int.to_s)
19
+ vm = host.get_monitored_vm(vmid)
20
+ command = MonitoredVmUtil.command_line(vm)
21
+ if command_pattern === command
22
+ return ConnectorAddressLink.import_from(vmid_int)
23
+ end
24
+ end
25
+
26
+ nil
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,69 @@
1
+
2
+ module JMX
3
+ module JDKHelper
4
+ module JDK6
5
+ java_import com.sun.tools.attach.VirtualMachine
6
+
7
+ class << self
8
+ def find_local_url(command_pattern)
9
+ target_vmd = VirtualMachine.list.find do |vmd|
10
+ command_pattern === vmd.display_name
11
+ end
12
+
13
+ if target_vmd
14
+ local_connector_address(target_vmd)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def local_connector_address(vm_descriptor)
21
+ vm = VirtualMachine.attach(vm_descriptor)
22
+
23
+ address = nil
24
+ agent_loaded = false
25
+
26
+ lambda {
27
+ address = vm.get_agent_properties.get(
28
+ "com.sun.management.jmxremote.localConnectorAddress")
29
+
30
+ unless address || agent_loaded
31
+ load_management_agent(vm)
32
+ agent_loaded = true
33
+ redo
34
+ end
35
+ }.call
36
+
37
+ vm.detach
38
+
39
+ address
40
+ end
41
+
42
+ def load_management_agent(vm)
43
+ home =
44
+ vm.get_system_properties.get_property 'java.home'
45
+
46
+ try_load_management_agent(vm, [home, 'jre', 'lib']) or
47
+ try_load_management_agent(vm, [home, 'lib']) or
48
+ raise "management agent not found"
49
+ end
50
+
51
+ def try_load_management_agent(vm, path)
52
+ sep = vm.get_system_properties.get_property 'file.separator'
53
+
54
+ path = path.dup
55
+ path << 'management-agent.jar'
56
+
57
+ file = Java::java.io.File.new(path.join(sep))
58
+ if file.exists
59
+ vm.load_agent(file.get_canonical_path,
60
+ "com.sun.management.jmxremote")
61
+ true
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+ end
69
+
@@ -0,0 +1,69 @@
1
+
2
+ module JMX
3
+ module JDKHelper
4
+ java_import java.lang.System
5
+
6
+ class << self
7
+
8
+ def method_missing(method, *args, &block)
9
+ init unless @jdk
10
+ @jdk.send method, *args, &block
11
+ end
12
+
13
+ private
14
+
15
+ def init
16
+ @jdk =
17
+ case
18
+ when has_java_class?("com.sun.tools.attach.VirtualMachine")
19
+ require "jdk/jdk6"
20
+ JDK6
21
+ when has_java_class?('sun.jvmstat.monitor.MonitoredHost')
22
+ require "jdk/jdk5"
23
+ JDK5
24
+ else
25
+ require "jdk/jdk4"
26
+ JDK4
27
+ end
28
+ end
29
+
30
+ def has_java_class?(name)
31
+ begin
32
+ java_import name
33
+ true
34
+ rescue
35
+ retry if load_tools_jar
36
+ false
37
+ end
38
+ end
39
+
40
+ def load_tools_jar
41
+ unless @tools_loaded
42
+ home = System.get_property 'java.home'
43
+ paths = [
44
+ [home, '..', 'lib'],
45
+ [home, 'lib'],
46
+ ]
47
+ try_load_jar('tools.jar', paths)
48
+ @tools_loaded = true
49
+ true
50
+ end
51
+ end
52
+
53
+ def try_load_jar(jar_file, paths)
54
+ sep = System.get_property 'file.separator'
55
+ paths = paths.dup
56
+ begin
57
+ path = paths.shift
58
+ require path.join(sep) + sep + jar_file
59
+ true
60
+ rescue LoadError
61
+ retry unless paths.empty?
62
+ false
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
69
+
@@ -0,0 +1,304 @@
1
+ # Copyright 2007 Jeff Mesnil (http://jmesnil.net)
2
+ require 'java'
3
+
4
+ class String
5
+ # Transform a CamelCase String to a snake_case String.
6
+ #--
7
+ # Code has been taken from ActiveRecord
8
+ def snake_case
9
+ self.to_s.gsub(/::/, '/').
10
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
11
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
12
+ tr("-", "_").
13
+ downcase
14
+ end
15
+
16
+ # Transform a snake_case String to a camelCase String with a lowercase initial.
17
+ #--
18
+ # Code has been taken from ActiveSupport
19
+ def camel_case
20
+ name = gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
21
+ name[0].chr.downcase + name[1..-1]
22
+ end
23
+ end
24
+
25
+ module JMX
26
+ require 'dynamic_mbean'
27
+ require 'open_data_helper'
28
+ require 'objectname_helper'
29
+ require 'jdk_helper'
30
+ require 'jruby'
31
+ require 'jars/wlthint3client.jar'
32
+
33
+ class MBeanServerConnectionProxy
34
+ attr_reader :connector
35
+
36
+ # Adds a connector attribute to Java's native MBeanServerConnection class.
37
+ #
38
+ # The connector attribute can be used to manage the connection (e.g, to close it).
39
+ # Why this isn't included in the native MBeanServerConnection class is beyond me.
40
+ #
41
+ # connector:: JMXConnector instance as returned by JMXConnectorFactory.connect.
42
+ def initialize(connector)
43
+ @connector = connector
44
+ @connection = connector.getMBeanServerConnection
45
+ end
46
+
47
+ # Close the connection (an unfortunate omission from the MBeanServerConnection class, imho)
48
+ def close
49
+ @connector.close
50
+ end
51
+
52
+ # Forward all other method messages to the underlying MBeanServerConnection instance.
53
+ def method_missing(method, *args, &block)
54
+ @connection.send method, *args, &block
55
+ end
56
+ end
57
+
58
+ class MBean
59
+ java_import java.util.HashMap
60
+ java_import javax.naming.Context
61
+ java_import javax.management.Attribute
62
+ java_import javax.management.ObjectName
63
+ java_import javax.management.remote.JMXConnector
64
+ java_import javax.management.remote.JMXConnectorFactory
65
+ java_import javax.management.remote.JMXServiceURL
66
+ JThread = java.lang.Thread
67
+
68
+ attr_reader :object_name, :operations, :attributes, :connection
69
+
70
+ def metaclass; class << self; self; end; end
71
+ def meta_def name, &blk
72
+ metaclass.instance_eval do
73
+ define_method name, &blk
74
+ end
75
+ end
76
+
77
+ # Creates a new MBean.
78
+ #
79
+ # object_name:: a string corresponding to a valid ObjectName
80
+ # connection:: a connection to a MBean server. If none is passed,
81
+ # use the global connection created by
82
+ # MBean.establish_connection
83
+ def initialize(object_name, connection=nil)
84
+ @connection = connection || @@connection
85
+ @object_name = object_name
86
+ info = @connection.getMBeanInfo @object_name
87
+ @attributes = Hash.new
88
+ info.attributes.each do | mbean_attr |
89
+ @attributes[mbean_attr.name.snake_case] = mbean_attr.name
90
+ end
91
+ @operations = Hash.new
92
+ info.operations.each do |mbean_op|
93
+ param_types = mbean_op.signature.map {|param| param.type}
94
+ @operations[mbean_op.name.snake_case] = [mbean_op.name, param_types]
95
+ end
96
+ end
97
+
98
+ def method_missing(method, *args, &block) #:nodoc:
99
+ method_in_snake_case = method.to_s.snake_case # this way Java/JRuby styles are compatible
100
+
101
+ if @operations.keys.include?(method_in_snake_case)
102
+ op_name, param_types = @operations[method_in_snake_case]
103
+ @connection.invoke @object_name,
104
+ op_name,
105
+ args.to_java(:Object),
106
+ param_types.to_java(:String)
107
+ else
108
+ super
109
+ end
110
+ end
111
+
112
+ @@connection = nil
113
+
114
+ # establish a connection to a remote MBean server which will
115
+ # be used by all subsequent MBeans.
116
+ #
117
+ # See MBean.create_connection for a list of the keys that are
118
+ # accepted in arguments.
119
+ #
120
+ # Examples
121
+ #
122
+ # JMX::MBean.establish_connection :port => "node23", :port => 1090
123
+ # JMX::MBean.establish_connection :port => "node23", :username => "jeff", :password => "secret"
124
+ # JMX::MBean.establish_connection :command => /jconsole/i
125
+ def self.establish_connection(args={})
126
+ @@connection ||= create_connection args
127
+ end
128
+
129
+ def self.remove_connection(args={})
130
+ if @@connection
131
+ @@connection.close rescue nil
132
+ end
133
+ @@connection = nil
134
+ end
135
+
136
+ def self.connection(args={})
137
+ if args.has_key? :host or args.has_key? :port
138
+ return create_connection(args)
139
+ else
140
+ @@connection ||= MBean.establish_connection(args)
141
+ end
142
+ end
143
+
144
+ # Create a connection to a remote MBean server.
145
+ #
146
+ # The args accepts the following keys:
147
+ #
148
+ # [:host] the host of the MBean server (defaults to "localhost")
149
+ #
150
+ # [:port] the port of the MBean server (defaults to 3000)
151
+ #
152
+ # [:url] the url of the MBean server.
153
+ # No default.
154
+ # if the url is specified, the host & port parameters are
155
+ # not taken into account
156
+ #
157
+ # [:command] the pattern matches the command line of the local
158
+ # JVM process including the MBean server.
159
+ # (command lines are listed on the connection dialog
160
+ # in JConsole).
161
+ # No default.
162
+ # this feature needs a JDK (>=5) installed on the local
163
+ # system.
164
+ # if the command is specified, the host & port or the url
165
+ # parameters are not taken into account
166
+ #
167
+ # [:username] the name of the user (if the MBean server requires authentication).
168
+ # No default
169
+ #
170
+ # [:password] the password of the user (if the MBean server requires authentication).
171
+ # No default
172
+ #
173
+ # [:credentials] custom credentials (if the MBean server requires authentication).
174
+ # No default. It has precedence over :username and :password (i.e. if
175
+ # :credentials is specified, :username & :password are ignored)
176
+ #
177
+ # [:provider_package] use to fill the JMXConnectorFactory::PROTOCOL_PROVIDER_PACKAGES.
178
+ # No default
179
+ #
180
+ def self.create_connection(args={})
181
+ host= args[:host] || "localhost"
182
+ port = args[:port] || 3000
183
+ username = args[:username]
184
+ password = args[:password]
185
+ credentials = args[:credentials]
186
+ provider_package = args[:provider_package]
187
+
188
+ if args[:command]
189
+ url = JDKHelper.find_local_url(args[:command]) or
190
+ raise "no locally attacheable VMs"
191
+ else
192
+ # host & port are not taken into account if url is set (see issue #7)
193
+ standard_url = "service:jmx:rmi:///jndi/rmi://#{host}:#{port}/jmxrmi"
194
+ url = args[:url] || standard_url
195
+ end
196
+
197
+ unless credentials
198
+ if !username.nil? and username.length > 0
199
+ user_password_credentials = [username, password]
200
+ credentials = user_password_credentials.to_java(:String)
201
+ end
202
+ end
203
+
204
+ env = HashMap.new
205
+ env.put(JMXConnector::CREDENTIALS, credentials) if credentials
206
+ # only fill the Context and JMXConnectorFactory properties if provider_package is set
207
+ if provider_package
208
+ env.put(Context::SECURITY_PRINCIPAL, username) if username
209
+ env.put(Context::SECURITY_CREDENTIALS, password) if password
210
+ env.put(JMXConnectorFactory::PROTOCOL_PROVIDER_PACKAGES, provider_package)
211
+ end
212
+
213
+ # the context class loader is set to JRuby's classloader when
214
+ # creating the JMX Connection so that classes loaded using
215
+ # JRuby "require" (and not from its classpath) can also be
216
+ # accessed (see issue #6)
217
+ begin
218
+ context_class_loader = JThread.current_thread.context_class_loader
219
+ JThread.current_thread.context_class_loader = JRuby.runtime.getJRubyClassLoader
220
+
221
+ puts url
222
+ puts $CLASSPATH
223
+
224
+ connector = JMXConnectorFactory::connect JMXServiceURL.new(url), env
225
+ MBeanServerConnectionProxy.new connector
226
+ ensure
227
+ # ... and we reset the previous context class loader
228
+ JThread.current_thread.context_class_loader = context_class_loader
229
+ end
230
+ end
231
+
232
+ # Returns an array of MBeans corresponding to all the MBeans
233
+ # registered for the ObjectName passed in parameter (which may be
234
+ # a pattern).
235
+ #
236
+ # The args accepts the same keys than #create_connection and an
237
+ # additional one:
238
+ #
239
+ # [:connection] a MBean server connection (as returned by #create_connection)
240
+ # No default. It has precedence over :host and :port (i.e if
241
+ # :connection is specified, :host and :port are ignored)
242
+ #
243
+ def self.find_all_by_name(name, args={})
244
+ object_name = ObjectName.new(name)
245
+ connection = args[:connection] || MBean.connection(args)
246
+ object_names = connection.queryNames(object_name, nil)
247
+ object_names.map { |on| create_mbean on, connection }
248
+ end
249
+
250
+ # Same as #find_all_by_name but the ObjectName passed in parameter
251
+ # can not be a pattern.
252
+ # Only one single MBean is returned.
253
+ def self.find_by_name(name, args={})
254
+ connection = args[:connection] || MBean.connection(args)
255
+ create_mbean ObjectName.new(name), connection
256
+ end
257
+
258
+ def self.create_mbean(object_name, connection)
259
+ info = connection.getMBeanInfo object_name
260
+ mbean = MBean.new object_name, connection
261
+ # define attribute accessor methods for the mbean
262
+ info.attributes.each do |mbean_attr|
263
+ mbean.meta_def mbean_attr.name.snake_case do
264
+ connection.getAttribute object_name, mbean_attr.name
265
+ end
266
+ if mbean_attr.isWritable
267
+ mbean.meta_def "#{mbean_attr.name.snake_case}=" do |value|
268
+ attribute = Attribute.new mbean_attr.name, value
269
+ connection.setAttribute object_name, attribute
270
+ end
271
+ end
272
+ end
273
+ mbean
274
+ end
275
+
276
+ def self.pretty_print (object_name, args={})
277
+ connection = args[:connection] || MBean.connection(args)
278
+ info = connection.getMBeanInfo ObjectName.new(object_name)
279
+ puts "object_name: #{object_name}"
280
+ puts "class: #{info.class_name}"
281
+ puts "description: #{info.description}"
282
+ puts "operations:"
283
+ info.operations.each do | op |
284
+ puts " #{op.name}"
285
+ op.signature.each do | param |
286
+ puts " #{param.name} (#{param.type} #{param.description})"
287
+ end
288
+ puts " ----"
289
+ puts " description: #{op.description}"
290
+ puts " return_type: #{op.return_type}"
291
+ puts " impact: #{op.impact}"
292
+ end
293
+ puts "attributes:"
294
+ info.attributes.each do | attr |
295
+ puts " #{attr.name}"
296
+ puts " description: #{attr.description}"
297
+ puts " type: #{attr.type}"
298
+ puts " readable: #{attr.readable}"
299
+ puts " writable: #{attr.writable}"
300
+ puts " is: #{attr.is}"
301
+ end
302
+ end
303
+ end
304
+ end