weblogic-jmx4r 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +202 -0
- data/README.rdoc +45 -0
- data/Rakefile +39 -0
- data/examples/class_loading.rb +16 -0
- data/examples/jvm_mngmt.rb +12 -0
- data/examples/logging.rb +26 -0
- data/examples/memory.rb +21 -0
- data/examples/memory_on_many_nodes.rb +44 -0
- data/examples/memory_types.rb +9 -0
- data/examples/ruby_mbean.rb +41 -0
- data/examples/runtime_sysprops.rb +14 -0
- data/examples/weblogic.rb +127 -0
- data/lib/dynamic_mbean.rb +277 -0
- data/lib/jconsole.rb +75 -0
- data/lib/jdk/jdk4.rb +16 -0
- data/lib/jdk/jdk5.rb +34 -0
- data/lib/jdk/jdk6.rb +69 -0
- data/lib/jdk_helper.rb +69 -0
- data/lib/jmx4r.rb +304 -0
- data/lib/jmx4r/version.rb +3 -0
- data/lib/objectname_helper.rb +21 -0
- data/lib/open_data_helper.rb +50 -0
- data/test/tc_attributes.rb +44 -0
- data/test/tc_auth.rb +70 -0
- data/test/tc_composite_data.rb +77 -0
- data/test/tc_connection.rb +92 -0
- data/test/tc_dynamic_mbean.rb +157 -0
- data/test/tc_methods.rb +30 -0
- data/test/tc_multiple_connections.rb +66 -0
- data/test/ts_all.rb +12 -0
- metadata +81 -0
data/lib/jdk/jdk4.rb
ADDED
@@ -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
|
+
|
data/lib/jdk/jdk5.rb
ADDED
@@ -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
|
+
|
data/lib/jdk/jdk6.rb
ADDED
@@ -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
|
+
|
data/lib/jdk_helper.rb
ADDED
@@ -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
|
+
|
data/lib/jmx4r.rb
ADDED
@@ -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
|