手工部署步驟繁瑣且易出錯:
當同一應用需部署多實例 (不同 JNDI、不同資料庫),或頻繁發布,JMX 自動化 成為唯一可行方案。
Properties props = new Properties();
props.setProperty(AdminClient.CONNECTOR_TYPE, AdminClient.CONNECTOR_TYPE_SOAP);
props.setProperty(AdminClient.CONNECTOR_HOST, "192.168.1.100");
props.setProperty(AdminClient.CONNECTOR_PORT, "8880");
AdminClient adminClient = AdminClientFactory.createAdminClient(props);
ConfigService configService = new ConfigServiceProxy(adminClient);
Session session = new Session();
// 2.1 定位/建立 JDBC Provider
ObjectName[] providers = configService.resolve(session,
"Cell=myCell:Node=myNode:JDBCProvider");
ObjectName jdbcProvider = findOrCreateJDBCProvider(configService, session, providers);
// 2.2 定位/建立 Data Source
ObjectName[] dataSources = configService.resolve(session,
"Cell=myCell:Node=myNode:JDBCProvider=" + jdbcProvider.getKeyProperty("name") + ":DataSource");
ObjectName dataSource = findOrCreateDataSource(configService, session, dataSources, jdbcProvider);
// 2.3 儲存配置
configService.save(session, true);
configService.discard(session);
// 3.1 取得部署任務
AppDeploymentTask task = appMgmt.createTask("install", earFilePath);
// 3.2 修改綁定 (資源參考、EJB 參考、虛擬主機、上下文根)
task.setProperty("bindings.resourceRefs[0].jndiName", "jdbc/MyAppDS");
task.setProperty("bindings.ejbRefs[0].jndiName", "ejb/MyService");
// ... 其他綁定
// 3.3 執行部署 (非同步 + 事件監聽)
appMgmt.performTask(task, new NotificationListener() {
@Override
public void handleNotification(Notification notif, Object handback) {
AppNotification evt = (AppNotification) notif.getUserData();
if (evt.getTaskName().equals(AppNotification.INSTALL)) {
if (evt.getTaskStatus().equals(AppNotification.STATUS_COMPLETED)) {
System.out.println("部署成功");
adminClient.removeNotificationListener(appMgmtMBean, this);
} else if (evt.getTaskStatus().equals(AppNotification.STATUS_FAILED)) {
System.err.println("部署失敗: " + evt.getMessage());
adminClient.removeNotificationListener(appMgmtMBean, this);
}
}
}
});
// 解壓 EAR → 修改 META-INF/application.xml 或 WAR 內 ibm-web-bnd.xmi
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(webXmlFile);
NodeList contextRoots = doc.getElementsByTagName("context-root");
contextRoots.item(0).setTextContent("/myapp-v2");
// 重新封裝 EAR
開始 ↓ 連線 AdminClient (SOAP) ↓ 檢查/建立 JDBC Provider ↓ 檢查/建立 Data Source (JNDI 名稱參數化) ↓ 儲存配置變更 ↓ 解壓 EAR → 修改綁定描述檔 (XSLT/JDOM) ↓ 建立 AppDeploymentTask ↓ 設定資源/EJB/虛擬主機綁定 ↓ 啟動非同步部署 + 註冊 NotificationListener ↓ 等待完成事件 → 成功/失敗處理 ↓ (可選) 啟動應用 ↓ 結束
| 難點 | 對策 |
|---|---|
| ObjectName 路徑複雜多變 | 封裝路徑建構工具類、使用 queryNames 模糊查詢 |
| 配置衝突 (同名資源) | 先查詢再決定建立/更新、使用唯一前綴命名 |
| EAR 綁定修改細節繁多 | 建立範本 EAR、使用 XSLT 標準化轉換 |
| 非同步部署狀態追蹤 | NotificationListener + 狀態機處理各階段事件 |
| 叢集環境同步 | 針對 DeploymentManager 操作、使用 sync 操作 |
WebSphere Application Server JMX API Referencewsadmin 腳本語言 (Jython/Jacl) 亦可完成同樣任務