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