/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program is distributed
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.plugin.weblogic.jmx;
import java.io.IOException;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.modelmbean.ModelMBeanInfo;
import javax.management.remote.JMXConnector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.appdef.shared.AIPlatformValue;
import org.hyperic.hq.appdef.shared.AIServerExtValue;
import org.hyperic.hq.appdef.shared.AIServiceTypeValue;
import org.hyperic.hq.appdef.shared.AIServiceValue;
import org.hyperic.hq.plugin.weblogic.WeblogicAuth;
import org.hyperic.hq.plugin.weblogic.WeblogicDetector;
import org.hyperic.hq.plugin.weblogic.WeblogicMetric;
import org.hyperic.hq.plugin.weblogic.WeblogicProductPlugin;
import org.hyperic.hq.plugin.weblogic.WeblogicUtil;
import org.hyperic.hq.product.Collector;
import org.hyperic.hq.product.GenericPlugin;
import org.hyperic.hq.product.PluginException;
import org.hyperic.hq.product.PluginUpdater;
import org.hyperic.hq.product.ProductPlugin;
import org.hyperic.hq.product.RuntimeDiscoverer;
import org.hyperic.hq.product.RuntimeResourceReport;
import org.hyperic.hq.product.ServerTypeInfo;
import org.hyperic.hq.product.ServiceType;
import org.hyperic.hq.product.jmx.MBeanUtil;
import org.hyperic.hq.product.jmx.ServiceTypeFactory;
import org.hyperic.util.config.ConfigResponse;
import org.hyperic.util.config.EncodingException;
public class WeblogicRuntimeDiscoverer implements RuntimeDiscoverer, PrivilegedAction {
private static final Double DYNAMIC_SVC_SUPPORTED_WEBLOGIC_VERSION = new Double(9.1d);
private static final boolean useJAAS = WeblogicProductPlugin.useJAAS();
private static final String PROP_FQDN = "weblogic.discover.fqdn";
private static Log log = LogFactory.getLog(WeblogicRuntimeDiscoverer.class);
private int serverId;
private Properties props;
private AIPlatformValue aiplatform;
private ConfigResponse config;
private String targetFqdn = null;
private WeblogicDetector plugin;
private String version;
private ServiceTypeFactory serviceTypeFactory = new ServiceTypeFactory();
private PluginUpdater pluginUpdater = new PluginUpdater();
public WeblogicRuntimeDiscoverer(WeblogicDetector plugin) {
this.plugin = plugin;
this.version = plugin.getTypeInfo().getVersion();
props = plugin.getManager().getProperties();
// this property can be used to host foreign nodes on
// another platform. if set to "same" will use the same fqdn
// as the admin server.
this.targetFqdn = props.getProperty(PROP_FQDN);
}
public WeblogicDetector getPlugin() {
return this.plugin;
}
public String getPluginVersion() {
return this.version;
}
public RuntimeResourceReport discoverResources(int serverId, AIPlatformValue aiplatform, ConfigResponse config)
throws PluginException {
if (useJAAS) {
return discoverAs(serverId, aiplatform, config);
}
else {
try {
return discover(serverId, aiplatform, config);
}
catch (SecurityException e) {
String msg = "SecurityException: " + e.getMessage();
throw new PluginException(msg, e);
}
}
}
private RuntimeResourceReport discoverAs(int serverId, AIPlatformValue aiplatform, ConfigResponse config)
throws PluginException {
Object obj;
WeblogicAuth auth = WeblogicAuth.getInstance(config.toProperties());
this.serverId = serverId;
this.aiplatform = aiplatform;
this.config = config;
try {
obj = auth.runAs(this);
}
catch (SecurityException e) {
throw new PluginException(e.getMessage(), e);
}
if (obj instanceof RuntimeResourceReport) {
return (RuntimeResourceReport) obj;
}
if (obj instanceof PluginException) {
throw (PluginException) obj;
}
if (obj instanceof Exception) {
Exception e = (Exception) obj;
throw new PluginException(e.getMessage(), e);
}
throw new IllegalArgumentException();
}
public Object run() {
try {
return discover(this.serverId, this.aiplatform, this.config);
}
catch (Exception e) {
return e;
}
}
private RuntimeResourceReport discover(int serverId, AIPlatformValue aiplatform, ConfigResponse config)
throws PluginException {
String installpath = config.getValue(ProductPlugin.PROP_INSTALLPATH);
// incase jaas is disabled; generatePlatform may use this.
this.aiplatform = aiplatform;
log.debug("discover using: " + config);
String domainName = config.getValue(WeblogicMetric.PROP_DOMAIN);
String serverName = config.getValue(WeblogicMetric.PROP_SERVER);
RuntimeResourceReport rrr = new RuntimeResourceReport(serverId);
Properties props = config.toProperties();
WeblogicDiscover discover = new WeblogicDiscover(this.version,props);
try {
MBeanServer mServer = discover.getMBeanServer();
discover.init(mServer);
NodeManagerQuery nodemgrQuery = new NodeManagerQuery();
ServerQuery serverQuery = new ServerQuery();
serverQuery.setDiscover(discover);
ArrayList servers = new ArrayList();
discover.find(mServer, serverQuery, servers);
WeblogicQuery[] serviceQueries = discover.getServiceQueries();
// ensure admin is first incase we need version
serverQuery.sort(servers);
String adminVersion = null;
for (int i = 0; i < servers.size(); i++) {
serverQuery = (ServerQuery) servers.get(i);
if(!serverQuery.isRunning()) continue;
if (serverQuery.isAdmin()) {
adminVersion = serverQuery.getVersion();
}
else if (serverQuery.getVersion() == null) {
// will be the case if a server was create but never started
// safely assume it was created by the admin server in this
// case and hence is the same version.
serverQuery.setVersion(adminVersion);
}
AIPlatformValue aPlatform;
AIServerExtValue aServer = generateServer(serverQuery);
// Set the ID, so when this report is processed at the server,
// there is no mistaking this server in the report for any other
// server
// in appdef (or worse, not matching anything and adding a new
// server).
if (serverQuery.getName().equals(serverName)
&& serverQuery.getDiscover().getDomain().equals(domainName)) {
aServer.setId(new Integer(serverId));
aServer.setPlaceholder(true);
// maintain existing installpath,
// MBeanServer CurrentDirectory might be different
if (installpath != null) {
aServer.setInstallPath(installpath);
}
aiplatform.addAIServerValue(aServer);
rrr.addAIPlatform(aiplatform);
}
else {
aPlatform = generatePlatform(serverQuery);
aPlatform.addAIServerValue(aServer);
rrr.addAIPlatform(aPlatform);
}
if (!serverQuery.isRunning()) {
continue;
}
mServer = discover.getMBeanServer(serverQuery.getUrl());
ArrayList aServices = new ArrayList();
ArrayList services = new ArrayList();
for (int j = 0; j < serviceQueries.length; j++) {
WeblogicQuery serviceQuery = serviceQueries[j];
serviceQuery.setParent(serverQuery);
serviceQuery.setVersion(serverQuery.getVersion());
discover.find(mServer, serviceQuery, services);
}
// Dynamic services can only be discovered on WebLogic 9.1 or
// higher
if (DYNAMIC_SVC_SUPPORTED_WEBLOGIC_VERSION.compareTo(Double.valueOf(serverQuery.getVersion())) <= 0) {
final JMXConnector jmxConnector = WeblogicUtil.getManagedServerConnection(props);
Set serviceTypes = new HashSet();
try {
discoverDynamicServices(discover, jmxConnector.getMBeanServerConnection(), serverQuery, services, serviceTypes);
}
catch (IOException e) {
throw new PluginException("Error discovering dynamic services", e);
}
finally {
try {
jmxConnector.close();
}
catch (IOException e) {
}
}
final AIServiceTypeValue[] serviceTypeValues = new AIServiceTypeValue[serviceTypes.size()];
int count = 0;
for(Iterator iterator = serviceTypes.iterator();iterator.hasNext();) {
serviceTypeValues[count] = ((ServiceType)iterator.next()).getAIServiceTypeValue();
count++;
}
aServer.setAiServiceTypes(serviceTypeValues);
pluginUpdater.updateServiceTypes(plugin.getProductPlugin(), serviceTypes);
}
for (int k = 0; k < services.size(); k++) {
boolean valid = true;
ServiceQuery service = (ServiceQuery) services.get(k);
if (service instanceof ApplicationQuery) {
valid = ((ApplicationQuery) service).isEAR();
}
if (valid) {
aServices.add(generateService(service));
} else {
log.debug("skipped service:"+service.getName());
}
}
AIServiceValue[] aiservices = (AIServiceValue[]) aServices.toArray(new AIServiceValue[0]);
aServer.setAIServiceValues(aiservices);
if (serverQuery.isAdmin()) {
ArrayList mgrs = new ArrayList();
nodemgrQuery.setAdminServer(serverQuery);
discover.find(mServer, nodemgrQuery, mgrs);
for (int n = 0; n < mgrs.size(); n++) {
nodemgrQuery = (NodeManagerQuery) mgrs.get(n);
aServer = generateServer(nodemgrQuery);
aPlatform = generatePlatform(nodemgrQuery);
aPlatform.addAIServerValue(aServer);
rrr.addAIPlatform(aPlatform);
}
}
}
}
catch (WeblogicDiscoverException e) {
throw new PluginException(e.getMessage(), e);
}
return rrr;
}
private void discoverDynamicServices(WeblogicDiscover discover, MBeanServerConnection mServer, ServerQuery parent,
ArrayList services, Set serviceTypes) throws PluginException, WeblogicDiscoverException {
try {
final Set objectNames = mServer.queryNames(new ObjectName(MBeanUtil.DYNAMIC_SERVICE_DOMAIN + ":*"), null);
//Only WebLogic Admin servers have auto-inventory plugins - have to construct a ServerInfo for the WebLogic server
String[] platformTypes = ((ServerTypeInfo)plugin.getTypeInfo()).getPlatformTypes();
ServerTypeInfo server =
new ServerTypeInfo(parent.getResourceType(), parent.getDescription(), parent.getVersion());
server.setValidPlatformTypes(platformTypes);
for (Iterator iterator = objectNames.iterator(); iterator.hasNext();) {
final ObjectName objectName = (ObjectName) iterator.next();
final MBeanInfo serviceInfo = mServer.getMBeanInfo(objectName);
if (serviceInfo instanceof ModelMBeanInfo) {
ServiceType identityType = serviceTypeFactory.getServiceType(plugin.getProductPlugin().getName(),server,
(ModelMBeanInfo) serviceInfo, objectName);
//identityType could be null if MBean is not to be exported
if(identityType != null) {
ServiceType serviceType;
if (!serviceTypes.contains(identityType)) {
serviceType = serviceTypeFactory.create(plugin.getProductPlugin(),
server, (ModelMBeanInfo) serviceInfo,
objectName);
if (serviceType != null) {
serviceTypes.add(serviceType);
}
}else {
serviceType = findServiceType(identityType.getInfo().getName(),serviceTypes);
}
final String shortServiceType = identityType.getServiceName();
DynamicServiceQuery dynamicServiceQuery = new DynamicServiceQuery();
dynamicServiceQuery.setParent(parent);
dynamicServiceQuery.setType(shortServiceType);
dynamicServiceQuery.setAttributeNames(serviceType.getCustomProperties().getOptionNames());
dynamicServiceQuery.setName(objectName.getKeyProperty("name"));
dynamicServiceQuery.getDynamicAttributes(mServer, objectName);
services.add(dynamicServiceQuery);
}
}
}
} catch (Exception e) {
throw new PluginException(e.getMessage(), e);
}
}
private ServiceType findServiceType(String serviceName, Set serviceTypes) {
for(Iterator iterator = serviceTypes.iterator();iterator.hasNext();) {
ServiceType serviceType = (ServiceType)iterator.next();
if(serviceType.getInfo().getName().equals(serviceName)) {
return serviceType;
}
}
return null;
}
private AIPlatformValue generatePlatform(BaseServerQuery server) {
AIPlatformValue aiplatform = new AIPlatformValue();
String serverFqdn = server.getFqdn();
serverFqdn = //support mapping via agent.properties
this.props.getProperty(PROP_FQDN + "." + serverFqdn,
serverFqdn);
String fqdn = serverFqdn;
// let it be known if the platform does not exist in cam
// since ai will ka-boom with huge "No such entity!" stacktrace.
// XXX ai should handle this condition better
if (!fqdn.equals(this.aiplatform.getFqdn())) {
log.info("Discovered server (" + server.getResourceFullName() + ") hosted on another platform (fqdn="
+ fqdn + ")");
}
if (this.targetFqdn != null) {
if (this.targetFqdn.equals("same")) {
fqdn = this.aiplatform.getFqdn();
}
else {
fqdn = this.targetFqdn;
}
}
else {
// out-of-the-box weblogic w/o changing ListenAddress
if (serverFqdn.equals("localhost") || serverFqdn.equals("127.0.0.1")) {
fqdn = this.aiplatform.getFqdn();
}
else {
fqdn = serverFqdn;
}
}
if (!fqdn.equals(serverFqdn)) {
log.info("changing fqdn for " + server.getName() + ": " + serverFqdn + " => " + fqdn);
}
aiplatform.setFqdn(fqdn);
return aiplatform;
}
private AIServerExtValue generateServer(BaseServerQuery server) throws PluginException {
AIServerExtValue aiserver = new AIServerExtValue();
aiserver.setInstallPath(server.getInstallPath());
aiserver.setAutoinventoryIdentifier(server.getIdentifier());
aiserver.setServicesAutomanaged(true);
aiserver.setServerTypeName(server.getResourceName());
String name = server.getResourceFullName();
if (WeblogicProductPlugin.usePlatformName) {
name = GenericPlugin.getPlatformName() + " " + name;
}
aiserver.setName(name);
String notes = server.getDescription();
if (notes != null) {
aiserver.setDescription(notes);
}
if (!server.isAdmin()) {
ConfigResponse productConfig = new ConfigResponse(server.getResourceConfig());
String listeningPorts = server.getAttribute(BaseServerQuery.ATTR_LISTEN_PORT);
if (listeningPorts!=null && !listeningPorts.equals("")) {
productConfig.setValue(Collector.LISTEN_PORTS, listeningPorts);
}
ConfigResponse metricConfig = new ConfigResponse();
ConfigResponse cprops = new ConfigResponse(server.getCustomProperties());
try {
if (server.hasControl() && !server.isServer61()) {
ConfigResponse controlConfig = new ConfigResponse(server.getControlConfig());
aiserver.setControlConfig(controlConfig.encode());
}
aiserver.setProductConfig(productConfig.encode());
aiserver.setMeasurementConfig(metricConfig.encode());
aiserver.setCustomProperties(cprops.encode());
}
catch (EncodingException e) {
throw new PluginException("Error generating config", e);
}
}
log.debug("discovered server: " + aiserver.getName());
return aiserver;
}
private AIServiceValue generateService(ServiceQuery service) throws PluginException {
AIServiceValue aiservice = new AIServiceValue();
ConfigResponse productConfig = new ConfigResponse(service.getResourceConfig());
ConfigResponse metricConfig = new ConfigResponse();
ConfigResponse cprops = new ConfigResponse(service.getCustomProperties());
String notes = service.getDescription();
if (notes != null) {
aiservice.setDescription(notes);
}
aiservice.setServiceTypeName(service.getResourceName());
String name = service.getResourceFullName();
if (WeblogicProductPlugin.usePlatformName) {
name = GenericPlugin.getPlatformName() + " " + name;
}
if (name.length() >= 200) {
// make sure we dont exceed service name limit
name = name.substring(0, 199);
}
aiservice.setName(name);
try {
if (service.hasControl() && !service.isServer61()) {
ConfigResponse controlConfig = new ConfigResponse(service.getControlConfig());
aiservice.setControlConfig(controlConfig.encode());
}
aiservice.setProductConfig(productConfig.encode());
aiservice.setMeasurementConfig(metricConfig.encode());
aiservice.setCustomProperties(cprops.encode());
if (service.hasResponseTime()) {
ConfigResponse rtConfig = new ConfigResponse(service.getResponseTimeConfig());
aiservice.setResponseTimeConfig(rtConfig.encode());
}
}
catch (EncodingException e) {
throw new PluginException("Error generating config", e);
}
log.debug("discovered service: " + aiservice.getName());
return aiservice;
}
}