/* * 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.bizapp.client; import java.io.IOException; import java.net.ConnectException; import java.util.HashSet; import javax.net.ssl.SSLException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.agent.AgentKeystoreConfig; import org.hyperic.hq.agent.stats.AgentStatsCollector; import org.hyperic.hq.bizapp.agent.ProviderInfo; import org.hyperic.hq.bizapp.shared.lather.CommandInfo; import org.hyperic.hq.bizapp.shared.lather.SecureAgentLatherValue; import org.hyperic.lather.LatherRemoteException; import org.hyperic.lather.LatherValue; import org.hyperic.lather.client.LatherHTTPClient; /** * Central place for communication back to the server. */ public abstract class AgentCallbackClient { private static final int TIMEOUT_CONN = 30 * 1000; private static final int TIMEOUT_DATA = 5 * 60 * 1000; private static final Log log = LogFactory.getLog(AgentCallbackClient.class); private static final String LATHER_CMD = "LATHER_CMD"; private ProviderFetcher fetcher; // Storage of provider info private HashSet<String> secureCommands; // Secure commands private static AgentStatsCollector statsCollector = AgentStatsCollector.getInstance(); static { statsCollector.register(LATHER_CMD); for (String cmd : CommandInfo.ALL_COMMANDS) { statsCollector.register(LATHER_CMD + "_" + cmd.toUpperCase()); } } public AgentCallbackClient(ProviderFetcher fetcher, String[] secureCommands) { this.fetcher = fetcher; this.resetProvider(); this.secureCommands = new HashSet<String>(); for(int i=0; i<secureCommands.length; i++){ this.secureCommands.add(secureCommands[i]); } } public AgentCallbackClient(ProviderFetcher fetcher){ this(fetcher, CommandInfo.SECURE_COMMANDS); } void resetProvider(){ } /** * Get the most up-to-date information about what our provider is, * from the storage provider. * * @return the string provider (such as jnp:stuff or http:otherstuff) */ protected ProviderInfo getProvider() throws AgentCallbackClientException { ProviderInfo val = this.fetcher.getProvider(); if(val == null){ final String msg = "Unable to communicate with server -- " + "provider not yet setup"; throw new AgentCallbackClientException(msg); } return val; } /** * Check to see if a particular provider URL is valid (i.e. something * that we know about && can process. */ public static boolean isValidProviderURL(String provider){ return provider.startsWith("http:") || provider.startsWith("https:"); } /** * Generate a provider URL given a host and port. This routine * adds in the prefix (such as http:, etc.) as well as the URL * after the host to identify the server interface (if necessary) * * @param host Host to generate provider for * @param port Port to use for provider. If it is -1, the default * port will be used. */ public static String getDefaultProviderURL(String host, int port, boolean secure) { String proto; if(port == -1){ port = secure ? 7443 : 7080; } proto = secure ? "https" : "http"; return proto + "://" + host + ":" + port + "/lather"; } /** * Retrieve the host name from a provider URL. * * @param providerURL The provider URL. * @return The host name. */ public static String getHostFromProviderURL(String providerURL) { int startIndex = providerURL.indexOf(':')+3; int endIndex = providerURL.indexOf(':', startIndex); return providerURL.substring(startIndex, endIndex); } protected LatherValue invokeLatherCall(ProviderInfo provider, String methodName, LatherValue args) throws AgentCallbackClientException { return invokeLatherCall(provider, methodName, args, (new AgentKeystoreConfig()).isAcceptUnverifiedCert()); } protected LatherValue invokeLatherCall(ProviderInfo provider, String methodName, LatherValue args, final boolean acceptUnverifiedCertificates) throws AgentCallbackClientException { LatherHTTPClient client; final boolean debug = log.isDebugEnabled(); String addr = provider.getProviderAddress(); if (this.secureCommands.contains(methodName)) { final String agentToken = provider.getAgentToken(); ((SecureAgentLatherValue)args).setAgentToken(agentToken); } try { client = new LatherHTTPClient(addr, TIMEOUT_CONN, TIMEOUT_DATA, acceptUnverifiedCertificates); final long start = now(); LatherValue rtn = client.invoke(methodName, args); final long duration = now()-start; statsCollector.addStat(duration, LATHER_CMD); statsCollector.addStat(duration, LATHER_CMD + "_" + methodName.toUpperCase()); return rtn; } catch(SSLException e) { if (debug) log.debug(e,e); throw new AgentCallbackClientException(e); } catch(ConnectException exc) { // All exceptions are logged as debug. If the caller wants to // log the exception message, it can. final String eMsg = "Unable to contact server @ " + addr + ": " + exc; if (debug) log.debug(eMsg); throw new AgentCallbackClientException(eMsg); } catch(IOException exc) { String msg = exc.getMessage(); if (msg != null) { String eMsg; if (msg.indexOf("Service Unavailable") != -1) { eMsg = "Unable to contact server -- it has no more free connections"; if (debug) log.debug(eMsg); } else { eMsg = "IO error: " + exc.getMessage(); if (debug) log.debug(eMsg); } throw new AgentCallbackClientException(eMsg); } if (debug) log.debug("IO error", exc); throw new AgentCallbackClientException("IO error: " + exc.getMessage()); } catch(LatherRemoteException exc) { String eMsg; if (exc.getMessage().indexOf("Unauthorized agent denied") != -1) { eMsg = "Unable to invoke '" + methodName + "': Permission denied"; if (debug) log.debug(eMsg); } else { eMsg = "Remote error while invoking '" + methodName + ": " + exc; if (debug) log.debug(eMsg); } throw new AgentCallbackClientException(eMsg, exc); } catch(IllegalStateException e) { if (debug) log.debug("Could not create the LatherHTTPClient instance", e); throw new AgentCallbackClientException(e); } } private long now() { return System.currentTimeMillis(); } }