JMX(java Management Exetensions)在Java编程语言中定义了应用程序以及网络管理和监控的体系结构、设计模式、应用程序接口以及服务。 通常使用JMX来监控系统的运行状态或管理系统的某些方面,比如清空缓存、重新加载配置文件等 优点是可以非常容易的使应用程序被管理 伸缩性的架构使每个JMX Agent Service可以很容易的放入到Agent中,每个JMX的实现都提供几个核心的Agent Service,你也可以自己编写服务,服务可以很容易的部署,取消部署。 主要作用是提供接口,允许有不同的实现 简单来说,jmx是一个用来管理javaBean并可以进行监控的扩展规范,结合MBeanServer、rmi与http等可以作为一个服务监控和提供中心
MBeanServer是JMX代理的核心组件。 它是在代理中向管理操作公开的对象的注册表。 向MBeanServer注册的任何对象都对管理应用程序可见。 MBeanServer仅公开MBean的管理接口,而不是它的直接对象引用。 您要从代理的Java VM外部管理的任何资源都必须在MBeanServer中注册为MBean。 MBeanServer还提供标准化接口,用于访问同一Java VM中的MBean,为本地对象提供操作可管理资源的所有好处。 需要注意的是,一般不使用MBeanServerFactory.createMBeanServer(),使用ManagementFactory.getPlatformMBeanServer() 后者在当前尚未在ManagementFactory中注册静态成员MBeanServer时,会先使用MBeanServerFactory.createMBeanServer()创建一个MBeanServer并将其注册到静态成员,后续每次调用会直接返回该成员实例 并且ManagementFactory.getPlatformMBeanServer()方法在第一次调用createMBeanServer()实例化MBeanServer以后,会读取PlatformComponent枚举的枚举值,将一些系统必要的MBean注册到MBeanServer JConsole监控的MBeanServer就是在ManagementFactory中注册的静态成员MBeanServe 所以如果没有特殊配置的MBeanServer,jconsole是不会监控的
Java Management Extensions(JMX)Agent是一个在Java虚拟机(Java VM)中运行的管理实体. 充当MBean和管理应用程序(JConsole等)之间的联络人 Agent只是一个规范,一般会封装我们创建和启动MBeanServer以及注册MBean的过程在一个Agent行为里,方便启动
Agent Service是可以对MBeanServer中注册的MBean执行管理操作的对象。 通过将管理智能包含在代理中,JMX可帮助您构建更强大的管理解决方案。 Agent Service通常也是MBean,允许通过MBeanServer控制它们及其功能。 JMX规范定义了以下Agent Service: 通过管理applet(m-let)服务的动态类加载检索并实例化从网络动态下载的新类和本机库。 监视器观察MBean属性的数字或字符串值,并可以向其他对象通知几种类型的更改。 定时器提供调度机制,并且可以以预定间隔发送通知。 关系服务定义MBean之间的关联并维护关系的一致性。
Protocol adaptors and connectors使代理可从远程(通过rmi或者http等协议)管理应用程序访问。 它们通过在MBean服务器中实例化和注册的MBean的特定协议提供视图。 它们使Java VM外部的管理应用程序能够: 获取或设置现有MBean的属性 对现有MBean执行操作 实例化并注册新的MBean 注册并接收MBean发出的通知 因此,要使JMX代理易于管理,它必须至少包含一个协议适配器或连接器。 Java SE平台包括标准RMI连接器。Agent可以包括任意数量的协议适配器和连接器,允许通过不同的协议同时远程管理和监视它
Protocol Adaptors通过给定协议提供JMX代理的管理视图。 它们将MBean和MBean服务器的操作调整为给定协议中的表示, 并可能调整为不同的信息模型,例如SNMP。Java SE平台不包括任何协议适配器作为标准。 连接到Protocol Adaptors的管理应用程序通常特定于给定的协议。 这通常是依赖于特定管理协议的传统管理解决方案的情况。 它们不是通过MBean服务器的远程表示来访问JMX代理,而是通过映射到MBeanServer的操作来访问JMX代理。
Connectors用于将代理与为JMX技术启用的远程管理应用程序连接,即使用JMX规范的分布式服务开发的管理应用程序。 这种通信涉及Agent中的连接器服务器和管理器中的连接器客户端。 这些组件以特定协议的方式透明地传递管理操作。 JMX Remote API为MBeanServer提供远程接口,管理应用程序可以通过该接口执行操作。 Connectors特定于给定协议,但管理应用程序可以无差别地使用任何Connectors,因为它们具有相同的远程接口。
描述一个可管理的资源。是一个java对象,遵循以下一些规则: 1.必须是公用的,非抽象的类 2.必须有至少一个公用的构造器 3.必须实现它自己的相应的MBean接口或者实现javax.management.DynamicMBean接口 4.可选的,一个MBean可以实现javax.management.NotificationBroadcaster接口MBean的类型 MBean使用ObjectName以keyValue形式注册到MBeanServer上
标准MBean,也是最简单的MBean,通过类实现的接口名称来识别MBean 接口名称以MBean结尾,实现类需要匹配接口名称的前半部分 ``` public interface YytMBean{ public void setName(String name); public String getName(); }
```
public class Yyt implements YytMBean{
private String name;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
}
public class YytAgent{
private String domainName;
private int rmiPort;
public YytAgent(String domainName,int rmiPort) {
this.domainName = domainName;
this.rmiPort = rmiPort;
}
public void start() throws MalformedObjectNameException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, IOException {
//MBeanServerFactory.createMBeanServer(); //don't use this
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); //use this instead
YytMBean yytMBean = new Yyt();
ObjectName objectName = new ObjectName(domainName+":name=Yyt");
mBeanServer.registerMBean(yytMBean, objectName);
LocateRegistry.createRegistry(rmiPort);//开启rmi端口监听,在jmxConnectorServer.start()时会根据serviceUrl建立一个rmiServer监听该rmi端口
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:"+rmiPort+"/"+domainName);
JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mBeanServer);
jmxConnectorServer.start();
//开启jmx协议的监控服务器,因为java自带rmi的工具和依赖,可以直接开启,通过jconsole等支持jmx协议的客户端可以监控MBeanServer
}
}
JConsole是java自带的监控程序,独立jre中没有,jdk下的jre具备
运行java应用时带上参数 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false 关闭ssl和密码校验,开启远程监控端口8999,这时可以直接使用jconsole进行链接
或者在jdk\jre\lib\management目录下,复制一份jmxremote.password.template的文件,改名去掉.template后缀,并去掉文件末尾两行示例的用户名和密码注释,修改密码为自己想要的 运行加上参数 -Djava.rmi.server.hostname=应用所在服务器的ip或者域名 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=监控端口 -Dcom.sun.management.jmxremote.rmi.port=rmi监控端口(一般与监控端口保持一致) -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=false 这时使用JConsole链接会提示需要输入用户名和密码
在命令行输入jconsole运行jconsole程序
jconsole] 在jconsole的界面,因为是本地,直接选择本地进程,进入监控页面 点击导航栏的MBean即可看到我们注册的MBean在列表中
jconsole 还可以对Bean的属性值进行查看和设置
jconsole]
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
Permission perm = new MBeanServerPermission("createMBeanServer");
sm.checkPermission(perm);
}
if (platformMBeanServer == null) {
platformMBeanServer = MBeanServerFactory.createMBeanServer();
for (PlatformComponent pc : PlatformComponent.values()) {
List<? extends PlatformManagedObject> list =
pc.getMXBeans(pc.getMXBeanInterface());
for (PlatformManagedObject o : list) {
// Each PlatformComponent represents one management
// interface. Some MXBean may extend another one.
// The MXBean instances for one platform component
// (returned by pc.getMXBeans()) might be also
// the MXBean instances for another platform component.
// e.g. com.sun.management.GarbageCollectorMXBean
//
// So need to check if an MXBean instance is registered
// before registering into the platform MBeanServer
if (!platformMBeanServer.isRegistered(o.getObjectName())) {
addMXBean(platformMBeanServer, o);
}
}
}
HashMap<ObjectName, DynamicMBean> dynmbeans =
ManagementFactoryHelper.getPlatformDynamicMBeans();
for (Map.Entry<ObjectName, DynamicMBean> e : dynmbeans.entrySet()) {
addDynamicMBean(platformMBeanServer, e.getValue(), e.getKey());
}
for (final PlatformManagedObject o :
ExtendedPlatformComponent.getMXBeans()) {
if (!platformMBeanServer.isRegistered(o.getObjectName())) {
addMXBean(platformMBeanServer, o);
}
}
}
return platformMBeanServer;
ManagementFactory中定义一个platformMBeanServer成员来装载创建好的MBeanServer 在执行该方法的第一步会先校验当前安全权限 在platformMBeanServer尚未绑定一个实例时,会先使用MBeanServerFactory.createMBeanServer()实例一个MBeanServer对象 接下来主要是读取PlatformComponent.values()以及别的枚举类和helper类的值和成员,将系统预设的MBean注册到MBeanServer
enum PlatformComponent {
/**
* Class loading system of the Java virtual machine.
*/
CLASS_LOADING(
"java.lang.management.ClassLoadingMXBean",
"java.lang", "ClassLoading", defaultKeyProperties(),
true, // singleton
new MXBeanFetcher<ClassLoadingMXBean>() {
public List<ClassLoadingMXBean> getMXBeans() {
return Collections.singletonList(ManagementFactoryHelper.getClassLoadingMXBean());
}
}),
/**
* Compilation system of the Java virtual machine.
*/
COMPILATION(
"java.lang.management.CompilationMXBean",
"java.lang", "Compilation", defaultKeyProperties(),
true, // singleton
new MXBeanFetcher<CompilationMXBean>() {
public List<CompilationMXBean> getMXBeans() {
CompilationMXBean m = ManagementFactoryHelper.getCompilationMXBean();
if (m == null) {
return Collections.emptyList();
} else {
return Collections.singletonList(m);
}
}
}),
.....
}
public static MBeanServer createMBeanServer() {
return createMBeanServer(null);
}
public static MBeanServer createMBeanServer(String domain) {
checkPermission("createMBeanServer");
final MBeanServer mBeanServer = newMBeanServer(domain);
addMBeanServer(mBeanServer);
return mBeanServer;
}
public static MBeanServer newMBeanServer(String domain) {
checkPermission("newMBeanServer");
// Get the builder. Creates a new one if necessary.
//
final MBeanServerBuilder mbsBuilder = getNewMBeanServerBuilder();
// Returned value cannot be null. NullPointerException if violated.
synchronized(mbsBuilder) {
final MBeanServerDelegate delegate =
mbsBuilder.newMBeanServerDelegate();
if (delegate == null) {
final String msg =
"MBeanServerBuilder.newMBeanServerDelegate() " +
"returned null";
throw new JMRuntimeException(msg);
}
final MBeanServer mbeanServer =
mbsBuilder.newMBeanServer(domain,null,delegate);
if (mbeanServer == null) {
final String msg =
"MBeanServerBuilder.newMBeanServer() returned null";
throw new JMRuntimeException(msg);
}
return mbeanServer;
}
}
createMBeanServer方法会调用newMBeanServer方法实例化MBeanServer newMBeanServer方法中会先使用MBeanServerBuilder.newMBeanServerDelegate()实例化一个MBeanServerBuilder 再使用MBeanServerBuilder.newMBeanServer(domain,null,delegate)方法实例化一个MBeanServer
public MBeanServer newMBeanServer(String defaultDomain,
MBeanServer outer,
MBeanServerDelegate delegate) {
// By default, MBeanServerInterceptors are disabled.
// Use com.sun.jmx.mbeanserver.MBeanServerBuilder to obtain
// MBeanServers on which MBeanServerInterceptors are enabled.
return JmxMBeanServer.newMBeanServer(defaultDomain,outer,delegate,
false);
}
newMBeanServer方法实际实例化并返回的是一个JmxMBeanServer对象
//in JmxMBeanServer
public static MBeanServer newMBeanServer(String defaultDomain,
MBeanServer outer,
MBeanServerDelegate delegate,
boolean interceptors) {
// Determine whether to use fair locking for the repository.
// Default is true.
final boolean fairLock = DEFAULT_FAIR_LOCK_POLICY;
checkNewMBeanServerPermission();
// This constructor happens to disregard the value of the interceptors
// flag - that is, it always uses the default value - false.
// This is admitedly a bug, but we chose not to fix it for now
// since we would rather not have anybody depending on the Sun private
// interceptor APIs - which is most probably going to be removed and
// replaced by a public (javax) feature in the future.
//
return new JmxMBeanServer(defaultDomain,outer,delegate,null,
interceptors,fairLock);
}
JmxMBeanServer(String domain, MBeanServer outer,
MBeanServerDelegate delegate,
MBeanInstantiator instantiator,
boolean interceptors,
boolean fairLock) {
if (instantiator == null) {
final ModifiableClassLoaderRepository
clr = new ClassLoaderRepositorySupport();
instantiator = new MBeanInstantiator(clr);
}
final MBeanInstantiator fInstantiator = instantiator;
this.secureClr = new
SecureClassLoaderRepository(AccessController.doPrivileged(new PrivilegedAction<ClassLoaderRepository>() {
@Override
public ClassLoaderRepository run() {
return fInstantiator.getClassLoaderRepository();
}
})
);
if (delegate == null)
delegate = new MBeanServerDelegateImpl();
if (outer == null)
outer = this;
this.instantiator = instantiator;
this.mBeanServerDelegateObject = delegate;
this.outerShell = outer;
final Repository repository = new Repository(domain);
this.mbsInterceptor =
new DefaultMBeanServerInterceptor(outer, delegate, instantiator,
repository);
this.interceptorsEnabled = interceptors;
initialize();
}
实例化一个Repository并聚合到新生成的一个DefaultMBeanServerInterceptor Repository就是MBeanServer存放MBean的容器类,其内部维护一个Map<string,map> domainTb的成员 此处由于是第一次实例化MBeanServer,传入的domain为null,最终会使用ServiceName的静态的成员值DefaultDomain作为默认域</string,map
public class ServiceName {
/**
* The default domain.
* <BR>
* The value is <CODE>DefaultDomain</CODE>.
*/
public static final String DOMAIN = "DefaultDomain";
.....
}
NamedObject类是聚合ObjectName和DynamicMBean的辅助类
public class NamedObject {
/**
* Object name.
*/
private final ObjectName name;
/**
* Object reference.
*/
private final DynamicMBean object;
.....
}
后续的注册bean和获取bean等方法实现最终都是由mbsInterceptor成员去操作
创建注册中心并开启端口监听,返回创建的RegistryImpl实例 源码分析请参照本人的另一篇博客——java-rmi源码解析java-rmi源码解析
在JmxMBeanServer中的实现
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException {
return mbsInterceptor.registerMBean(object, cloneObjectName(name));
}
上面讲到,默认的MBeanServer的mbsInterceptor成员由new DefaultMBeanServerInterceptor(outer, delegate, instantiator,repository)实例化
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException {
// ------------------------------
// ------------------------------
Class<?> theClass = object.getClass();
Introspector.checkCompliance(theClass);
final String infoClassName = getNewMBeanClassName(object);
checkMBeanPermission(infoClassName, null, name, "registerMBean");
checkMBeanTrustPermission(theClass);
return registerObject(infoClassName, object, name);
}
private ObjectInstance registerObject(String classname,
Object object, ObjectName name)
throws InstanceAlreadyExistsException,
MBeanRegistrationException,
NotCompliantMBeanException {
if (object == null) {
final RuntimeException wrapped =
new IllegalArgumentException("Cannot add null object");
throw new RuntimeOperationsException(wrapped,
"Exception occurred trying to register the MBean");
}
DynamicMBean mbean = Introspector.makeDynamicMBean(object);
return registerDynamicMBean(classname, mbean, name);
}
DefaultMBeanServerInterceptor会将要注册的MBean封装为DynamicMBean,调用registerDynamicMBean进行注册
private ObjectInstance registerDynamicMBean(String classname,
DynamicMBean mbean,
ObjectName name)
throws InstanceAlreadyExistsException,
MBeanRegistrationException,
NotCompliantMBeanException {
name = nonDefaultDomain(name);
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
MBEANSERVER_LOGGER.logp(Level.FINER,
DefaultMBeanServerInterceptor.class.getName(),
"registerMBean", "ObjectName = " + name);
}
ObjectName logicalName = preRegister(mbean, server, name);
// preRegister returned successfully, so from this point on we
// must call postRegister(false) if there is any problem.
boolean registered = false;
boolean registerFailed = false;
ResourceContext context = null;
try {
if (mbean instanceof DynamicMBean2) {
try {
((DynamicMBean2) mbean).preRegister2(server, logicalName);
registerFailed = true; // until we succeed
} catch (Exception e) {
if (e instanceof RuntimeException)
throw (RuntimeException) e;
if (e instanceof InstanceAlreadyExistsException)
throw (InstanceAlreadyExistsException) e;
throw new RuntimeException(e);
}
}
if (logicalName != name && logicalName != null) {
logicalName =
ObjectName.getInstance(nonDefaultDomain(logicalName));
}
checkMBeanPermission(classname, null, logicalName, "registerMBean");
if (logicalName == null) {
final RuntimeException wrapped =
new IllegalArgumentException("No object name specified");
throw new RuntimeOperationsException(wrapped,
"Exception occurred trying to register the MBean");
}
final Object resource = getResource(mbean);
// Register the MBean with the repository.
// Returns the resource context that was used.
// The returned context does nothing for regular MBeans.
// For ClassLoader MBeans the context makes it possible to register these
// objects with the appropriate framework artifacts, such as
// the CLR, from within the repository lock.
// In case of success, we also need to call context.done() at the
// end of this method.
//
context = registerWithRepository(resource, mbean, logicalName);
registerFailed = false;
registered = true;
} finally {
try {
postRegister(logicalName, mbean, registered, registerFailed);
} finally {
if (registered && context!=null) context.done();
}
}
return new ObjectInstance(logicalName, classname);
}
registerDynamicMBean方法最终调用registerWithRepository方法将DynamicMBean对象注册到Repository里
private ResourceContext registerWithRepository(
final Object resource,
final DynamicMBean object,
final ObjectName logicalName)
throws InstanceAlreadyExistsException,
MBeanRegistrationException {
// Creates a registration context, if needed.
//
final ResourceContext context =
makeResourceContextFor(resource, logicalName);
repository.addMBean(object, logicalName, context);
// May throw InstanceAlreadyExistsException
// ---------------------
// Send create event
// ---------------------
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
MBEANSERVER_LOGGER.logp(Level.FINER,
DefaultMBeanServerInterceptor.class.getName(),
"addObject", "Send create notification of object " +
logicalName.getCanonicalName());
}
sendNotification(
MBeanServerNotification.REGISTRATION_NOTIFICATION,
logicalName);
return context;
}
前面已经讲过在创建DefaultMBeanServerInterceptor实例时,会实例化一个repository对象并装载到成员 注册bean会调用repository的addMBean方法
public void addMBean(final DynamicMBean object, ObjectName name,
final RegistrationContext context)
throws InstanceAlreadyExistsException {
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(),
"addMBean", "name = " + name);
}
// Extract the domain name.
String dom = name.getDomain().intern();
boolean to_default_domain = false;
// Set domain to default if domain is empty and not already set
if (dom.length() == 0)
name = Util.newObjectName(domain + name.toString());
// Do we have default domain ?
if (dom == domain) { // ES: OK (dom & domain are interned)
to_default_domain = true;
dom = domain;
} else {
to_default_domain = false;
}
// Validate name for an object
if (name.isPattern()) {
throw new RuntimeOperationsException(
new IllegalArgumentException("Repository: cannot add mbean for " +
"pattern name " + name.toString()));
}
lock.writeLock().lock();
try {
// Domain cannot be JMImplementation if entry does not exist
if ( !to_default_domain &&
dom.equals("JMImplementation") &&
domainTb.containsKey("JMImplementation")) {
throw new RuntimeOperationsException(
new IllegalArgumentException(
"Repository: domain name cannot be JMImplementation"));
}
// If domain does not already exist, add it to the hash table
final Map<String,NamedObject> moiTb = domainTb.get(dom);
if (moiTb == null) {
addNewDomMoi(object, dom, name, context);
return;
} else {
// Add instance if not already present
String cstr = name.getCanonicalKeyPropertyListString();
NamedObject elmt= moiTb.get(cstr);
if (elmt != null) {
throw new InstanceAlreadyExistsException(name.toString());
} else {
nbElements++;
addMoiToTb(object,name,cstr,moiTb,context);
}
}
} finally {
lock.writeLock().unlock();
}
}
private void addMoiToTb(final DynamicMBean object,
final ObjectName name,
final String key,
final Map<String,NamedObject> moiTb,
final RegistrationContext context) {
registering(context);
moiTb.put(key,new NamedObject(name, object));
}
domainTb成员就是前面说过的repository中存储MBean的Map,类型是Map<string,map> add方法里会通过writelock进行同步 如果获取到的moiTb,代表该MBean的域名尚未加入domainTb,会将域名加入map并将MBean也加入</string,map
JmxMBeanServer 不提供直接返回MBean对象的方法 但提供了很多行为如 setAttributes,getAttribute,invoke等方法,根据ObjectName参数进行MBean识别 调用成员的DefaultMBeanServerInterceptor实例去执行对应的修改属性,获取属性值,执行方法等行为 DefaultMBeanServerInterceptor最终是获取repository中的MBean去执行对应行为 通过getObjectInstance可以获得该MBean的类名和域名信息的封装对象
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException {
return mbsInterceptor.getObjectInstance(cloneObjectName(name));
}
public Object getAttribute(ObjectName name, String attribute)
throws MBeanException, AttributeNotFoundException,
InstanceNotFoundException, ReflectionException {
return mbsInterceptor.getAttribute(cloneObjectName(name), attribute);
}
public void setAttribute(ObjectName name, Attribute attribute)
throws InstanceNotFoundException, AttributeNotFoundException,
InvalidAttributeValueException, MBeanException,
ReflectionException {
mbsInterceptor.setAttribute(cloneObjectName(name),
cloneAttribute(attribute));
}
public Object invoke(ObjectName name, String operationName,
Object params[], String signature[])
throws InstanceNotFoundException, MBeanException,
ReflectionException {
return mbsInterceptor.invoke(cloneObjectName(name), operationName,
params, signature);
}
该方法创建一个JMXConnectorServer,将MBeanServer和JMXServiceURL传递过去 最终url、MbeanServer等对象会被聚合到JMXConnectorServer,作为成员
public static JMXConnectorServer
newJMXConnectorServer(JMXServiceURL serviceURL,
Map<String,?> environment,
MBeanServer mbeanServer)
throws IOException {
Map<String, Object> envcopy;
if (environment == null)
envcopy = new HashMap<String, Object>();
else {
EnvHelp.checkAttributes(environment);
envcopy = new HashMap<String, Object>(environment);
}
final Class<JMXConnectorServerProvider> targetInterface =
JMXConnectorServerProvider.class;
final ClassLoader loader =
JMXConnectorFactory.resolveClassLoader(envcopy);
final String protocol = serviceURL.getProtocol();
final String providerClassName = "ServerProvider";
JMXConnectorServerProvider provider =
JMXConnectorFactory.getProvider(serviceURL,
envcopy,
providerClassName,
targetInterface,
loader);
IOException exception = null;
if (provider == null) {
// Loader is null when context class loader is set to null
// and no loader has been provided in map.
// com.sun.jmx.remote.util.Service class extracted from j2se
// provider search algorithm doesn't handle well null classloader.
if (loader != null) {
try {
JMXConnectorServer connection =
getConnectorServerAsService(loader,
serviceURL,
envcopy,
mbeanServer);
if (connection != null)
return connection;
} catch (JMXProviderException e) {
throw e;
} catch (IOException e) {
exception = e;
}
}
provider =
JMXConnectorFactory.getProvider(
protocol,
PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
JMXConnectorFactory.class.getClassLoader(),
providerClassName,
targetInterface);
}
if (provider == null) {
MalformedURLException e =
new MalformedURLException("Unsupported protocol: " + protocol);
if (exception == null) {
throw e;
} else {
throw EnvHelp.initCause(e, exception);
}
}
envcopy = Collections.unmodifiableMap(envcopy);
return provider.newJMXConnectorServer(serviceURL,
envcopy,
mbeanServer);
}
JMXConnectorServerFactory的newJMXConnectorServer最终是返回由provider生产的JMXConnectorServer provider是接口,在jdk里对于该方法有两个实现类,一个是返回基于rmi协议,另一个是返回基于iiop协议的
//ServiceProvider for rmi
public JMXConnectorServer newJMXConnectorServer(JMXServiceURL serviceURL,
Map<String,?> environment,
MBeanServer mbeanServer)
throws IOException {
if (!serviceURL.getProtocol().equals("rmi")) {
throw new MalformedURLException("Protocol not rmi: " +
serviceURL.getProtocol());
}
return new RMIConnectorServer(serviceURL, environment, mbeanServer);
}
需要留意的是,在serviceProvider中调用生成RMIConnectorServer时,最终会调用传递(RMIServerImpl) null为形参参数的构造函数构造 也就是生成的RMIConnectorServer中是没有指定服务端对象的 RMIServerImpl会在JMXConnectorServer调用start方法后结合前面聚合到JMXConnectorServer中的rmi的url去实例化
public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment,
MBeanServer mbeanServer)
throws IOException {
this(url, environment, (RMIServerImpl) null, mbeanServer);
}
public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment,
RMIServerImpl rmiServerImpl,
MBeanServer mbeanServer)
throws IOException {
super(mbeanServer);
if (url == null) throw new
IllegalArgumentException("Null JMXServiceURL");
if (rmiServerImpl == null) {
final String prt = url.getProtocol();
if (prt == null || !(prt.equals("rmi") || prt.equals("iiop"))) {
final String msg = "Invalid protocol type: " + prt;
throw new MalformedURLException(msg);
}
final String urlPath = url.getURLPath();
if (!urlPath.equals("")
&& !urlPath.equals("/")
&& !urlPath.startsWith("/jndi/")) {
final String msg = "URL path must be empty or start with " +
"/jndi/";
throw new MalformedURLException(msg);
}
}
if (environment == null)
this.attributes = Collections.emptyMap();
else {
EnvHelp.checkAttributes(environment);
this.attributes = Collections.unmodifiableMap(environment);
}
this.address = url;
this.rmiServerImpl = rmiServerImpl;
}
启动JMX链接服务器,该过程会完成rmiServerImpl的实例化(开启监听rmi服务端口) 所以如果前面没有调用LocateRegistry.createRegistry(rmiPort)对端口进行注册,会导致报错
public synchronized void start() throws IOException {
final boolean tracing = logger.traceOn();
if (state == STARTED) {
if (tracing) logger.trace("start", "already started");
return;
} else if (state == STOPPED) {
if (tracing) logger.trace("start", "already stopped");
throw new IOException("The server has been stopped.");
}
if (getMBeanServer() == null)
throw new IllegalStateException("This connector server is not " +
"attached to an MBean server");
// Check the internal access file property to see
// if an MBeanServerForwarder is to be provided
//
if (attributes != null) {
// Check if access file property is specified
//
String accessFile =
(String) attributes.get("jmx.remote.x.access.file");
if (accessFile != null) {
// Access file property specified, create an instance
// of the MBeanServerFileAccessController class
//
MBeanServerForwarder mbsf;
try {
mbsf = new MBeanServerFileAccessController(accessFile);
} catch (IOException e) {
throw EnvHelp.initCause(
new IllegalArgumentException(e.getMessage()), e);
}
// Set the MBeanServerForwarder
//
setMBeanServerForwarder(mbsf);
}
}
try {
if (tracing) logger.trace("start", "setting default class loader");
defaultClassLoader = EnvHelp.resolveServerClassLoader(
attributes, getMBeanServer());
} catch (InstanceNotFoundException infc) {
IllegalArgumentException x = new
IllegalArgumentException("ClassLoader not found: "+infc);
throw EnvHelp.initCause(x,infc);
}
if (tracing) logger.trace("start", "setting RMIServer object");
final RMIServerImpl rmiServer;
if (rmiServerImpl != null)
rmiServer = rmiServerImpl;
else
rmiServer = newServer();
rmiServer.setMBeanServer(getMBeanServer());
rmiServer.setDefaultClassLoader(defaultClassLoader);
rmiServer.setRMIConnectorServer(this);
rmiServer.export();
try {
if (tracing) logger.trace("start", "getting RMIServer object to export");
final RMIServer objref = objectToBind(rmiServer, attributes);
if (address != null && address.getURLPath().startsWith("/jndi/")) {
final String jndiUrl = address.getURLPath().substring(6);
if (tracing)
logger.trace("start", "Using external directory: " + jndiUrl);
String stringBoolean = (String) attributes.get(JNDI_REBIND_ATTRIBUTE);
final boolean rebind = EnvHelp.computeBooleanFromString( stringBoolean );
if (tracing)
logger.trace("start", JNDI_REBIND_ATTRIBUTE + "=" + rebind);
try {
if (tracing) logger.trace("start", "binding to " + jndiUrl);
final Hashtable<?, ?> usemap = EnvHelp.mapToHashtable(attributes);
bind(jndiUrl, usemap, objref, rebind);
boundJndiUrl = jndiUrl;
} catch (NamingException e) {
// fit e in the nested exception if we are on 1.4
throw newIOException("Cannot bind to URL ["+jndiUrl+"]: "
+ e, e);
}
} else {
// if jndiURL is null, we must encode the stub into the URL.
if (tracing) logger.trace("start", "Encoding URL");
encodeStubInAddress(objref, attributes);
if (tracing) logger.trace("start", "Encoded URL: " + this.address);
}
} catch (Exception e) {
try {
rmiServer.close();
} catch (Exception x) {
// OK: we are already throwing another exception
}
if (e instanceof RuntimeException)
throw (RuntimeException) e;
else if (e instanceof IOException)
throw (IOException) e;
else
throw newIOException("Got unexpected exception while " +
"starting the connector server: "
+ e, e);
}
rmiServerImpl = rmiServer;
synchronized(openedServers) {
openedServers.add(this);
}
state = STARTED;
if (tracing) {
logger.trace("start", "Connector Server Address = " + address);
logger.trace("start", "started.");
}
}
前面讲过rmiServerImpl成员是空的,所以这时会调用newServer方法进行创建
RMIServerImpl newServer() throws IOException {
final boolean iiop = isIiopURL(address,true);
final int port;
if (address == null)
port = 0;
else
port = address.getPort();
if (iiop)
return newIIOPServer(attributes);
else
return newJRMPServer(attributes, port);
}
接下来会调用objectToBind 通过toStub方法创建当前的RMIServer对象的远程代理对象
private static RMIServer objectToBind(
RMIServerImpl rmiServer, Map<String, ?> env)
throws IOException {
return RMIConnector.
connectStub((RMIServer)rmiServer.toStub(),env);
}
public Remote toStub() throws IOException {
return RemoteObject.toStub(this);
}
public static Remote toStub(Remote obj) throws NoSuchObjectException {
if (obj instanceof RemoteStub ||
(obj != null &&
Proxy.isProxyClass(obj.getClass()) &&
Proxy.getInvocationHandler(obj) instanceof
RemoteObjectInvocationHandler))
{
return obj;
} else {
return sun.rmi.transport.ObjectTable.getStub(obj);
}
}
然后调用bind方法将RMIServer的远程代理对象注册到注册中心
void bind(String jndiUrl, Hashtable<?, ?> attributes,
RMIServer rmiServer, boolean rebind)
throws NamingException, MalformedURLException {
// if jndiURL is not null, we nust bind the stub to a
// directory.
InitialContext ctx =
new InitialContext(attributes);
if (rebind)
ctx.rebind(jndiUrl, rmiServer);
else
ctx.bind(jndiUrl, rmiServer);
ctx.close();
}
至此,完成了JmxConnectorServer的启动,程序可以通过jmx规范,访问MBeanServer与其上的MBean 关于remote和rmi的细节可以参考本人另一篇博客——java-rmi源码解析java-rmi源码解析