/* * 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-2013], 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.jboss7; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonParseException; import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Type; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.NoHttpResponseException; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.BasicHttpContext; import org.hyperic.hq.agent.AgentKeystoreConfig; import org.hyperic.hq.plugin.jboss7.objects.Connector; import org.hyperic.hq.plugin.jboss7.objects.DataSource; import org.hyperic.hq.plugin.jboss7.objects.DataSource71; import org.hyperic.hq.plugin.jboss7.objects.Deployment; import org.hyperic.hq.plugin.jboss7.objects.ServerInfo; import org.hyperic.hq.plugin.jboss7.objects.ServerMemory; import org.hyperic.hq.plugin.jboss7.objects.ThreadsInfo; import org.hyperic.hq.plugin.jboss7.objects.TransactionsStats; import org.hyperic.hq.plugin.jboss7.objects.WebSubsystem; import org.hyperic.hq.product.PluginException; import org.hyperic.util.config.ConfigResponse; import org.hyperic.util.http.HQHttpClient; import org.hyperic.util.http.HttpConfig; public final class JBossAdminHttp { private static final List<String> DATA_SOURCE_TYPES = Arrays.asList(new String[]{"data-source", "xa-data-source"}); private static final Log log = LogFactory.getLog(JBossAdminHttp.class); private final DefaultHttpClient client; private String user; private String pass; private BasicHttpContext localcontext; private HttpHost targetHost; private String hostName; private String serverName; public JBossAdminHttp(Properties props) throws PluginException { try { int port = Integer.parseInt(props.getProperty(JBossStandaloneDetector.PORT)); String addr = props.getProperty(JBossStandaloneDetector.ADDR); boolean https = "true".equals(props.getProperty(JBossStandaloneDetector.HTTPS)); this.user = props.getProperty(JBossStandaloneDetector.USERNAME); this.pass = props.getProperty(JBossStandaloneDetector.PASSWORD); this.hostName = props.getProperty(JBossStandaloneDetector.HOST); this.serverName = props.getProperty(JBossStandaloneDetector.SERVER); log.debug("props=" + props); targetHost = new HttpHost(addr, port, https ? "https" : "http"); log.debug("targetHost=" + targetHost); AgentKeystoreConfig config = new AgentKeystoreConfig(); client = new HQHttpClient(config, new HttpConfig(5000, 5000, null, 0), config.isAcceptUnverifiedCert()); if ((user != null) && (pass != null)) { client.getCredentialsProvider().setCredentials( new AuthScope(targetHost.getHostName(), targetHost.getPort()), new UsernamePasswordCredentials(user, pass)); } } catch (Throwable ex) { throw new PluginException(ex.getMessage(), ex); } // AuthCache authCache = new BasicAuthCache(); // DigestScheme digestAuth = new DigestScheme(); // digestAuth.overrideParamter("realm", "'TestRealm'"); // authCache.put(targetHost, digestAuth); // localcontext = new BasicHttpContext(); // localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache); } public JBossAdminHttp(ConfigResponse props) throws PluginException { this(props.toProperties()); } private String prepareURL() { String url = targetHost.toURI() + "/management"; if (hostName != null) { url += "/host/" + hostName; if (serverName != null) { url += "/server/" + serverName; } } return url; } private Object post(String api, Type type, Map args) throws PluginException { GsonBuilder gsb = new GsonBuilder(); Gson gson = gsb.create(); String argsJson = gson.toJson(args); log.debug("[post] argsJson="+argsJson); HttpPost post = new HttpPost(prepareURL() + api); post.setHeader("Content-Type", "application/json"); try { post.setEntity(new StringEntity(argsJson)); } catch (UnsupportedEncodingException ex) { log.debug(ex.getMessage(), ex); throw new PluginException(ex.getMessage(), ex); } return query(post, api, type); } private Object get(String api, Type type) throws PluginException { HttpGet get = new HttpGet(prepareURL() + api); return query(get, api, type); } private Object query(HttpRequestBase req, String api, Type type) throws PluginException { Object res = null; try { HttpResponse response = client.execute(req, localcontext); int statusCode = response.getStatusLine().getStatusCode(); // response must be read in order to "close" the connection. // https://jira.hyperic.com/browse/HHQ-5063#comment-154101 String responseBody = readInputString(response.getEntity().getContent()); if (log.isDebugEnabled()) { log.debug("[" + api + "] -(" + req.getURI() + ")-> " + responseBody); } if (statusCode != 200) { throw new PluginException("[" + req.getURI() + "] http error code: '" + statusCode + "' msg='" + response.getStatusLine().getReasonPhrase() + "'"); } GsonBuilder gsb = new GsonBuilder(); if (!((type instanceof Class) && ((Class) type).getCanonicalName().equals(Connector.class.getCanonicalName()))) { gsb.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES); } Gson gson = gsb.create(); res = gson.fromJson(responseBody, type); if (log.isDebugEnabled()) { if (res.getClass().isArray()) { log.debug("[" + api + "] -(" + statusCode + ")*> " + Arrays.asList((Object[]) res)); } else { log.debug("[" + api + "] -(" + statusCode + ")-> " + res); } } } catch (JsonParseException ex) { log.debug(ex.getMessage(), ex); throw new PluginException(ex.getMessage(), ex); } catch (IOException ex) { log.debug(ex.getMessage(), ex); throw new PluginException(ex.getMessage(), ex); } return res; } public WebSubsystem getWebSubsystem() throws PluginException { Type type = new TypeToken<WebSubsystem>() { }.getType(); return (WebSubsystem) get("/subsystem/web?recursive=true", type); } public Connector getConnector(String connector) throws PluginException { Type type = new TypeToken<Connector>() { }.getType(); try { connector = URLEncoder.encode(connector, "UTF-8"); } catch (UnsupportedEncodingException ex) { throw new RuntimeException(ex); } return (Connector) get("/subsystem/web/connector/" + connector + "?include-runtime=true", type); } public ThreadsInfo getThreadsInfo() throws PluginException { Type type = new TypeToken<ThreadsInfo>() { }.getType(); return (ThreadsInfo) get("/core-service/platform-mbean/type/threading", type); } public ServerMemory getServerMemory() throws PluginException { Type type = new TypeToken<ServerMemory>() { }.getType(); return (ServerMemory) get("/core-service/platform-mbean/type/memory?include-runtime=true", type); } public TransactionsStats getTransactionsStats() throws PluginException { Type type = new TypeToken<TransactionsStats>() { }.getType(); return (TransactionsStats) get("/subsystem/transactions?include-runtime=true", type); } public List<Deployment> getDeployments() throws PluginException { Type type = new TypeToken<List<Deployment>>() { }.getType(); return (List<Deployment>) get("/deployment/*?recursive=true", type); } public List<DS> getDatasources() throws PluginException { Type type = new TypeToken<Map<String, Map<String, String>>>() { }.getType(); Map<String, Map<String, String>> ds = (Map<String, Map<String, String>>) get("/subsystem/datasources", type); List<DS> res = new ArrayList<DS>(); for (String ds_type : DATA_SOURCE_TYPES) { if (ds.get(ds_type) != null) { for (String name : ds.get(ds_type).keySet()) { res.add(new DS(name, ds_type)); } } } return res; } public DataSource getDatasource(String dsType, String name, boolean runtime, String jbossVersion) throws PluginException { Type objectType; if (jbossVersion.equalsIgnoreCase("7")) { objectType = new TypeToken<DataSource>() { }.getType(); } else { objectType = new TypeToken<DataSource71>() { }.getType(); } try { name = URLEncoder.encode(name, "UTF-8"); } catch (UnsupportedEncodingException ex) { throw new RuntimeException(ex); } String args = ""; if (runtime) { args += "?include-runtime=true"; if (!jbossVersion.equalsIgnoreCase("7")) { args += "&recursive"; } } DataSource res = (DataSource) get("/subsystem/datasources/" + dsType + "/" + name + args, objectType); return res; } public void shutdown() throws PluginException { executeCommand("shutdown"); } public void stop() throws PluginException { executeCommand("stop"); } public void start() throws PluginException { executeCommand("start"); } public void restart() throws PluginException { executeCommand("restart"); } private void executeCommand(String command) throws PluginException { Type type = new TypeToken<Map>() { }.getType(); Map address = null; if (hostName != null) { address = new HashMap(); address.put("host", hostName); if (serverName != null) { address.put("server-config", serverName); } } Map args = new HashMap(); args.put("operation", command); if (address != null) { args.put("address", address); } try { Map res = (Map) post("", type, args); log.debug("[executeCommand]["+command+"] res=" + res); } catch (PluginException ex) { if ((ex.getCause() instanceof NoHttpResponseException) && (hostName != null)) { log.debug("[executeCommand]["+command+"] executed with execiton '" + ex.getMessage() + "' => maybe a JBoss bug"); } else { throw ex; } } } void testConnection() throws PluginException { Type type = new TypeToken<ServerInfo>() { }.getType(); get("/", type); } public static String readInputString(InputStream in) throws IOException { StringBuilder out = new StringBuilder(); byte[] b = new byte[4096]; for (int n; (n = in.read(b)) != -1;) { out.append(new String(b, 0, n)); } return out.toString(); } public class DS { String name, type; public DS(String name, String type) { this.name = name; this.type = type; } } }