WebSphere Application Server(以下简称为WAS)提供了两种管理途径: 基于Web方式的管理控制台和基于命令行方式的 wsadmin 工具;
还可以通过管理编程接口(WebSphere Management API)来开发符合特定需求的管理应用
WAS 的管理体系是基于 JMX 规范的,JMX 规范定义了三层结构:
Distributed layer:包含各种适配器,使得使用不同协议 ( 如 RMI,HTTP ) 的管理应用能访问代理层。
Agent layer:包含 Mbean Server,是 Mbean 的注册中心,操纵各种 Mbean 并对外提供各种管理服务,如监控,定时任务,Mbean 的动态加载等。
Instrumentation layer:包含各种 Mbean,Mbean 实现对资源的访问和代表了资源状态。 图 1.JMX 规范的三层结构示例
JMX 即 Java Management Extensions Java管理扩展 MBean 即 managed beans 被管理的Beans
一个MBean是一个被管理的Java对象,有点类似于JavaBean,一个设备、一个应用或者任何资源都可以被表示为MBean,MBean会暴露一个接口对外,这个接口可以读取或者写入一些对象中的属性,通常一个MBean需要定义一个接口,以MBean结尾, 例如: EchoMBean, 格式为XXXMBean,这个是规范,必须得遵守。描述一个可管理的资源。
是一个java对象,遵循以下一些规则:
1.必须是公用的,非抽象的类 2.必须有至少一个公用的构造器
3.必须实现它自己的相应的MBean接口或者实现javax.management.DynamicMBean接口 4.可选的,一个MBean可以实现javax.management.NotificationBroadcaster接口MBean的类型。
WAS 的管理体系,如图 -2 所示: 图 2.WAS 的管理体系
本文主要介绍上图中 custom client 的编程。管理编程分为下面两种途径: 1.直接调用 Mbean
2.使用 WAS 的管理编程接口
WebSphere JMX Mbeans Mbeans 介绍
WAS 提供了各种类型的 Mbean, 封装了各种管理功能,如对 WAS 系统环境,J2EE 应用,J2EE 资源等的管理和监控。
获得某个 Mbean:每个 MBean 都有一个 ObjectName,在使用该 MBean 之前必须先找到它。ObjectName 可以通过 AdminClient,以格式化的查询串进行查询。格式化的查询串可以选择性地包括以下的特性:域名、节点名、进程名、类型、名称等等。 一个查询串既可惟一标识单个 ObjectName,也可以代表多个具有公共特性的 ObjectName。MBean 通常是以它们的类型进行分类的。 一些 MBean 类型(例如 Perf)会在应用服务器中提供单个的
实例,但其他 MBean 类型(例如 servlet 和 EJB)会在应用服务器中提供多个实例。
清单 1. 获得某个 server 上的类型为 JVM 的 MBean
String query = “*:type=JVM,process=server1,node=node1,*\"” ; queryName = new ObjectName(query);
Set mBeans = null; try {
mBeans = adminClient.queryNames(queryName, null); } catch (ConnectorException e) { e.printStackTrace(); }
我们也可以基于 JMX connector specification 和 JMX Remote application programming interface (API) (JSR 160) 来访问 Mbean。 参见 WAS 信息中心文章 “Create a JMX remote client program by using the JMX remote API”。
在一个 WAS Network Deployment 环境中,我们可以通过 AdminClient 连接到 dmgr,也可以直接去连接某个 nodeagent 或 server。 如果我们连接的是 dmgr ,那么通过 dmgr 去访问 nodeagent 和 server 上的 MBean 时,前提条件是这个 nodeagent 或 server 必须是启动的。
访问 Mbean 的属性和方法 我们知道,调用 EJB 或 Web service 最终都要通过 Java 接口来进行, 但 Mbean 却不同。调用 Mbean 的方法与 java 反射机制类似,要提供方法名称,方法的参数类型,最后才能 invoke 该方法。
访问 Mbean 的普通属性:
清单 2. 访问 Mbean 的普通属性
Object heapsize = adminClient.getAttribute(mbean, \"heapSize\") ; 调用 Mbean 的方法:
清单 3. 调用 Mbean 的方法
ObjectName jvmBean = MBeanFactory.getInstance().getJVMMBean (server.nodeName, server.serverName) ; String signature[] = { \"java.lang.String\" };
server.hostIP = (String)adminClient.invoke(jvmBean, \"getIPAddress\ new Object[]{server.hostName}, signature) ;
WebSphere management API
WAS 提供的管理编程接口包含在以 com.ibm.websphere.management 开头的包中。下面介绍一下包中的实用 API。
AdminClient 和 AdminService
AdminClient 用于连接远程的 JVM,AdminService 用于连接本地 JVM,如果你的管理代码运行在和被管理的服务器相同的 JVM 内,那可以使用 AdminService 接口。AdminService 具有和 AdminClient 类似的方法,也能访问服务器上的 Mbean。 下面是获得 AdminClient 接口的代码: 清单 4. 获得 AdminClient 接口的代码 Properties props = new Properties();
props.put(AdminClient.CONNECTOR_TYPE, AdminClient.CONNECTOR_TYPE_SOAP); props.put(AdminClient.CONNECTOR_HOST, host.getHostIP()); props.put(AdminClient.CONNECTOR_PORT, host.getSoapport());
props.put(AdminClient.CONNECTOR_AUTO_ACCEPT_SIGNER, true);
if (isSecurityEnabled()) {
props.setProperty(AdminClient.CONNECTOR_SECURITY_ENABLED, Boolean.toString(true));
props.setProperty(AdminClient.USERNAME, host.getUsername()); props.setProperty(AdminClient.PASSWORD, host.getPasswd()); }
AdminClient 既可以运行在 admin thin client 中,也可以运行在 WAS server runtime 中。 获得 AdminService 接口 :
清单 5. 获得 AdminService 接口的代码
AdminService adminService = AdminServiceFactory.getAdminService() ;
Wsadmin API 和 management API 之间的对应关系
WAS 命令行脚本工具提供了几个管理对象 AdminApp, AdminTask, AdminConfig, AdminControl。它们分别对应不同的管理 API: 1. AdminApp:
AdminApp 对象的功能对应于两个包 com.ibm.websphere.management.application 和
com.ibm.websphere.management.application.client。 通过这两个包下面的 API,我们可以安装 / 卸载,启动 / 停止一个 J2EE 应用。和应用管理相关的编程比较固定,本文没有过多介绍这些 API 的使用,读者可以参考本文提供的例子 ApplicationMgr.java,里面包含了应用安装和启停的示例。
2. AdminControl: AdminControl 汇集了一些常用的 Mbean 的功能,如 Server Mbean。此外,它能直接调用任何一个 Mbean 对象。所以, AdminControl 提供的功能可以由 AdminClient/AdminService 来替换。
3. AdminConfig: AdminConfig 对象对应于两个包 com.ibm.websphere.management.configservice 和 com.ibm.websphere.management.configservice.tasks。
4. AdminTask: AdminTask 对象对应于两个包 com.ibm.websphere.management.cmdframework 和 com.ibm.websphere.management.cmdframework.provider。
本文主要介绍 AdminConfig 和 AdminTask 对应的管理编程。 使用 ConfigService
WAS 的配置信息有些可以通过 Mbean 去获得,如 cell/node/server 的名称,也可以通过 ConfigServer 去获取。而有些配置信息只能通过 ConfigService 去获得。 下面是使用 ConfigService 时会遇到的几个概念:
ConfigData :config data 对应于 config 目录下的某个 xml 文件或 xml 文件的某个片段,通过操纵 config data,就可以读写该 xml 文件。
ConfigType :config data 的类型。首先要知道你要访问配置信息的类型才能去访问。 通过 configService 的 getSupportedConfigObjectTypes() 方法可以获得所有的 ConfigData 的类型信息。例如我们要获得 WAS 上所有的数据源的配置信息,通过查找上述类型列表,可以知道数据源的 ConfigType 为 DataSource。 ConfigData Id :ConfigData 的唯一标识。 如何访问某个 ConfigData ?
通过 ConfigData type 来查询 ConfigData 通过 ConfigDataId 来创建 ConfigData
ConfigData 和 ConfigDataId 的关系如下:
清单 6. ConfigData 和 ConfigDataId 的转换关系 // 获得 ConfigData 的 ConfigDataId
ConfigDataId configDataId = ConfigServiceHelper.getConfigDataId(configData) ;
// 已知 ConfigDataId,得到其 ConfigData
public ObjectName createConfigData(String dataId) { ObjectName configData = null ;
ConfigDataId id = new ConfigDataId(dataId) ;
configData = ConfigServiceHelper.createObjectName(id) ; return configData ; }
使用 ConfigService 获得环境信息
下面举例说明如何用 MBean 和 ConfigService 获取集群信息,方便读者的对比。关于更多其它环境信息的获取,读者可以参考本文例子 WASEnvHelper.java。 清单 7. 通过 MBean 获得所有集群及集群成员 public void listClusterMembersFromMBean() {
Set clusters = queryMBeanList(\"WebSphere:type=Cluster,*\") ; Iterator ci = clusters.iterator() ;
String signature[] = { \"java.lang.String\" }; while (ci.hasNext()) { ObjectName cluMBean = (ObjectName) ci.next(); try { String clustername = cluMBean.getCanonicalName(); clustername = (String)adminClient.getAttribute(cluMBean, \"clusterName\") ;
System.out.println(clustername); ClusterMemberData[] membersData = (ClusterMemberData[])adminClient.invoke(cluMBean, \"getClusterMembers\ for (ClusterMemberData clusterMemberData : membersData) { System.out.println(clusterMemberData.memberName); } } catch (Exception e) { e.printStackTrace() ; } }
清单 8. 通过 ConfigService 获得所有集群及集群成员
public HashMap 创建新的配置 创建配置的难点在于确立 AttributeList 的结构,尤其是对于嵌套复杂的 AttributeList,往往需要先写个读取的程序来判断里面每个对象的类型。下面是创建 JDBC Provider 和 Datasource 的实例。注意,在不同的 scope 下都有 resources.xml,所以在查询 resources.xml 中的 ConfigData 时要先确定 scope。 清单 9. 通过 ConfigService 获得所有集群及集群成员 public void createJDBCResource(AdminClient adminClient) { Session session = new Session(); ConfigService configService = null ; try { configService = new ConfigServiceProxy(adminClient); ObjectName scope = configService.resolve(session, \"Node=kuNode05:Server=server1\")[0]; ObjectName pattern = ConfigServiceHelper.createObjectName(null, \"JDBCProvider\"); ObjectName[] configData = configService.queryConfigObjects(session, scope, pattern , null); ObjectName parent = configData[0] ; System.out.println(parent); AttributeList provAttrs = new AttributeList(); provAttrs.add(new Attribute(\"name\ provAttrs.add(new Attribute(\"implementationClassName\ com.mycompany.MyConnectionPoolDataSource\")); provAttrs.add(new Attribute(\"description\ //create the provider ObjectName jdbcProv = configService.createConfigData(session, scope, \"JDBCProvider\ \"JDBCProvider\ System.out.println(\"create the JDBC provider successfully.\"); configService.addElement(session,jdbcProv,\"classpath\ // Prepare the attribute list AttributeList dsAttrs = new AttributeList(); dsAttrs.add(new Attribute(\"name\ dsAttrs.add(new Attribute(\"jndiName\ //create a new datasource ObjectName dataSource = configService.createConfigData(session, jdbcProv, \"DataSource\ // Add a propertySet. AttributeList propSetAttrs = new AttributeList(); ObjectName resourcePropertySet = configService.createConfigData(session,dataSource, \"propertySet\ System.out.println(\"create the DataSource successfully.\"); configService.save(session, false); } catch (Exception ex) { ex.printStackTrace(); } } 修改已有配置 下面的 updateAttributelist 是一个通用的方法,相对于设置好 Attributelist 里的值之后做一个提交。 清单 10. 提交更新的通用方法 // 设置新的属性值 ConfigServiceHelper.setAttributeValue(threadpool, \"minimumSize\ new Integer((int)pool.getMinSize())) ; ConfigServiceHelper.setAttributeValue(threadpool, \"maximumSize\ new Integer((int)pool.getMaxSize())) ; // 更新整个 Attributelist updateAttributelist(threadpool) ; public void updateAttributelist(AttributeList attrs) { try { ConfigDataId id = ConfigServiceHelper.getConfigDataId(attrs) ; ObjectName objectName = ConfigServiceHelper.createObjectName(id) ; configService.setAttributes(session, objectName, attrs) ; }catch (Exception e) e.printStackTrace() ; } } 修改 ConfigData 的属性。修改 configdata 的某个属性时,需要先判断该属性的数据类型。最简单的办法是打印出该 configdata 的信息: 采用 System.out.println(obj); 和 System.out.println(obj.getClass().getName()); 即可。如果数据类型为 ObjectName,说明该属性值指向另一个 configdata,那我们在设置该属性的 value 之前, 需要先查询到那个 configdata。 清单 11. 修改 ConfigData 的属性 ObjectName pattern = ConfigServiceHelper.createObjectName(null, \"Security\"); ObjectName[] configData = configService.queryConfigObjects(session, null, pattern, null); String curID =\"\"; String ltpaID = \"\"; for (int i = 0; i < configData.length; i++) { System.out.println(configData[i]); Object obj = configService.getAttribute(session , configData[i], \"activeAuthMechanism\") ; Object id = ConfigServiceHelper.getConfigDataId((ObjectName)obj) ; System.out.println(id); //configService.getAttributes(session, configData[i], \"activeUserRegistry\ System.out.println(obj); System.out.println(obj.getClass().getName()); AttributeList enableSecurity = new AttributeList(); ObjectName pattern1 = ConfigServiceHelper.createObjectName(null, \"CustomUserRegistry\"); ObjectName[] configData1 = configService.queryConfigObjects(session, null, pattern1, null); //System.out.println(configData1[0]); ObjectName d = createConfigData(configDataId) ; enableSecurity.add(new Attribute(\"activeUserRegistry\ enableSecurity.add(new Attribute(\"cacheTimeout\ enableSecurity.add(new Attribute(\"enabled\ enableSecurity.add(new Attribute(\"appEnabled\ updateAttributelist(enableSecurity); } configService.save(session, false); 使用 Command Framework WAS 大量的管理功能包含在 wsadmin 的 AdminTask 对象中。WAS 提供了 command framework programming API,可以帮助我们开发等价于 AdminTask 脚本功能的程序。cmdframework 的命令名称通常就是 AdminTask 的方法名称,命令的参数与 AdminTask 方法的参数也一致,但需要做类型转化,即将 AdminTask 中 python 的数据类型转化成 Java 的数据类型,这种对应关系并不难确立。此外还需要注意一个 AdminTask 中是否包含 CommandStep ,可以通过 listCommandSteps() 检查 。 清单 12. 为指定的 role 分配用户组的 wsadmin 脚本 AdminTask.mapGroupsToNamingRole('[-roleName CosNamingWrite -specialSubjects [EVERYONE ]]') AdminConfig.save() 清单 13. 将上述 wsadmin 脚本转化为 Java 编程 // 下面的 import 实际上列出了 cmdframework 管理编程的常用类 import com.ibm.websphere.management.AdminClient; import com.ibm.websphere.management.AdminClientFactory; import com.ibm.websphere.management.Session; import com.ibm.websphere.management.cmdframework.AdminCommand; import com.ibm.websphere.management.cmdframework.CommandMgr; import com.ibm.websphere.management.cmdframework.CommandResult; import com.ibm.websphere.management.cmdframework.CommandStep; import com.ibm.websphere.management.cmdframework.TaskCommand; import com.ibm.websphere.management.async.client.AsyncCommandClient; public class CmdFrameworkSample { public static void main(String args[]) { AdminClient adminClient ; // 获得 adminClient CommandMgr cmdMgr = CommandMgr.getClientCommandMgr(adminClient); // 创建一个异步通知监听器 AsyncCmdTaskHandler listener = new AsyncCmdTaskHandler(); String cmdName = \"mapGroupsToNamingRole\"; AdminCommand cmd = cmdMgr.createCommand(cmdName); cmd.setConfigSession(session); cmd.setParameter(\"roleName\ cmd.setParameter(\"specialSubjects\ asyncCmdClientHelper.processCommandParameters(cmd); // Call the asynchronous command client to run the command. asyncCmdClientHelper.execute(cmd); // Check the command result. CommandResult result = cmd.getCommandResult(); if (result != null) { if (result.isSuccessful()) { System.out.println(\"Modified the CosNamingRole successfully.\"); }else { } } ConfigService configService = new ConfigServiceProxy(adminClient); configService.save(session, false); } } 清单 14. 通过 cmdframework 来添加 cluster member try { Session configSession = new Session(); AsyncCmdTaskHandler listener = new AsyncCmdTaskHandler(); AsyncCommandClient asyncCmdClientHelper = new AsyncCommandClient( session, listener); CommandMgr cmdMgr = CommandMgr.getCommandMgr(adminClient); TaskCommand createMemberCmd = (TaskCommand) cmdMgr createCommand(\"createClusterMember\"); createMemberCmd.setConfigSession(configSession); createMemberCmd.setParameter(\"clusterName\ }catch(Exception ex) { } CommandStep step1 = createMemberCmd.getCommandStep(\"memberConfig\"); step1.setParameter(\"memberNode\ step1.setParameter(\"memberName\ step1.setParameter(\"memberWeight\ step1.setParameter(\"genUniquePorts\ step1.setParameter(\"replicatorEntry\ asyncCmdClientHelper.processCommandParameters(createMemberCmd); // Call the asynchronous command client to run the command. asyncCmdClientHelper.execute(createMemberCmd); CommandResult res = createMemberCmd.getCommandResult(); if (!res.isSuccessful()) { System.out.println(res.getException().getMessage()); } else { System.out.println(\"Created successfully!\"); } //sync() ; configService.save(configSession, false); } catch (Throwable e) { e.printStackTrace() ; } 目前已经有大量的 wsadmin 脚本例子和脚本库可供 WAS 用户使用, 但基于管理编程接口编程的实例仍很稀少。本文总结了管理编程中最常用的几种接口:MBean,ConfigService 和 Cmdframework 的使用,以及将 wsadmin 脚本编程转化为 Java 编程的一般途径,为开发自定制管理客户端的人员提供了参考。 因篇幅问题不能全部显示,请点此查看更多更全内容