/** * Provides access to the native system. * * @author John Mazzitelli */ public class NativePromptCommand implements AgentPromptCommand { private static final Msg MSG = AgentI18NFactory.getMsg(); /** @see AgentPromptCommand#getPromptCommandString() */ public String getPromptCommandString() { return MSG.getMsg(AgentI18NResourceKeys.NATIVE); } /** @see AgentPromptCommand#execute(AgentMain, String[]) */ public boolean execute(AgentMain agent, String[] args) { PrintWriter out = agent.getOut(); if (args.length <= 1) { out.println( MSG.getMsg( SystemInfoFactory.isNativeSystemInfoAvailable() ? AgentI18NResourceKeys.NATIVE_IS_AVAILABLE : AgentI18NResourceKeys.NATIVE_IS_NOT_AVAILABLE)); out.println( MSG.getMsg( SystemInfoFactory.isNativeSystemInfoDisabled() ? AgentI18NResourceKeys.NATIVE_IS_DISABLED : AgentI18NResourceKeys.NATIVE_IS_NOT_DISABLED)); out.println( MSG.getMsg( SystemInfoFactory.isNativeSystemInfoInitialized() ? AgentI18NResourceKeys.NATIVE_IS_INITIALIZED : AgentI18NResourceKeys.NATIVE_IS_NOT_INITIALIZED)); return true; } processArguments(agent, args); return true; } /** @see AgentPromptCommand#getSyntax() */ public String getSyntax() { return MSG.getMsg(AgentI18NResourceKeys.NATIVE_SYNTAX); } /** @see AgentPromptCommand#getHelp() */ public String getHelp() { return MSG.getMsg(AgentI18NResourceKeys.NATIVE_HELP); } /** @see AgentPromptCommand#getDetailedHelp() */ public String getDetailedHelp() { return MSG.getMsg(AgentI18NResourceKeys.NATIVE_DETAILED_HELP); } private void processArguments(AgentMain agent, String[] args) { PrintWriter out = agent.getOut(); String sopts = "deop::sv"; LongOpt[] lopts = { new LongOpt("disable", LongOpt.NO_ARGUMENT, null, 'd'), new LongOpt("enable", LongOpt.NO_ARGUMENT, null, 'e'), new LongOpt("os", LongOpt.NO_ARGUMENT, null, 'o'), new LongOpt("ps", LongOpt.OPTIONAL_ARGUMENT, null, 'p'), new LongOpt("shutdown", LongOpt.NO_ARGUMENT, null, 's'), new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'v') }; Getopt getopt = new Getopt("native", args, sopts, lopts); int code; while ((code = getopt.getopt()) != -1) { switch (code) { case ':': case '?': case 1: { out.println(MSG.getMsg(AgentI18NResourceKeys.HELP_SYNTAX_LABEL, getSyntax())); break; } case 'd': { SystemInfoFactory.disableNativeSystemInfo(); out.println(MSG.getMsg(AgentI18NResourceKeys.NATIVE_DISABLE_DONE)); break; } case 'e': { SystemInfoFactory.enableNativeSystemInfo(); out.println(MSG.getMsg(AgentI18NResourceKeys.NATIVE_ENABLE_DONE)); break; } case 'o': { SystemInfo sysInfo = SystemInfoFactory.createSystemInfo(); // careful - I chose to only output things that I know the non-native Java sysinfo can // support out.println( MSG.getMsg( AgentI18NResourceKeys.NATIVE_OS_OUTPUT, sysInfo.getOperatingSystemName(), sysInfo.getOperatingSystemVersion(), sysInfo.getHostname())); break; } case 'p': { SystemInfo sysInfo = SystemInfoFactory.createSystemInfo(); String verboseOpt = getopt.getOptarg(); boolean verbose = (verboseOpt != null) && verboseOpt.equals(MSG.getMsg(AgentI18NResourceKeys.NATIVE_VERBOSE)); try { List<ProcessInfo> processes = sysInfo.getAllProcesses(); if (verbose) { out.println(MSG.getMsg(AgentI18NResourceKeys.NATIVE_PS_OUTPUT_VERBOSE_HEADER)); } else { out.println(MSG.getMsg(AgentI18NResourceKeys.NATIVE_PS_OUTPUT_SHORT_HEADER)); } for (ProcessInfo p : processes) { if (verbose) { out.println( MSG.getMsg( AgentI18NResourceKeys.NATIVE_PS_OUTPUT_VERBOSE, p.getPid(), p.getParentPid(), p.getBaseName(), Arrays.toString(p.getCommandLine()))); } else { out.println( MSG.getMsg( AgentI18NResourceKeys.NATIVE_PS_OUTPUT_SHORT, p.getPid(), p.getName())); } } } catch (Exception e) { out.println(MSG.getMsg(AgentI18NResourceKeys.NATIVE_NOT_SUPPORTED)); } break; } case 's': { if (!agent.isStarted()) { SystemInfoFactory.shutdown(); SystemInfoFactory.disableNativeSystemInfo(); out.println(MSG.getMsg(AgentI18NResourceKeys.NATIVE_SHUTDOWN_DONE)); } else { out.println(MSG.getMsg(AgentI18NResourceKeys.NATIVE_SHUTDOWN_FAILED_AGENT_STARTED)); } break; } case 'v': { out.println(SystemInfoFactory.getNativeSystemInfoVersion()); break; } } } if ((getopt.getOptind() + 1) < args.length) { out.println(MSG.getMsg(AgentI18NResourceKeys.HELP_SYNTAX_LABEL, getSyntax())); } return; } }
/** * This thread's job is to periodically try to get the agent to point back to its primary server, if * it isn't pointing to that server already. * * <p>The "primary server" is the server found at the top of the agent's failover list. If the agent * is already talking to this server, or if the agent does not yet have a failover list, nothing * needs to be done. * * <p>If the agent is talking to another server, this thread will probe the primary server and if it * can, this thread will switch the agent's sender back to point to the primary. * * <p>If the agent is not in sending mode, this thread will not do anything until it is. The agent * will decide what server it should talk to in that case. This thread is only here to prevent an * agent talking to a non-primary server for a long time when the primary server is available. * * @author John Mazzitelli */ public class PrimaryServerSwitchoverThread extends Thread { private static final Logger LOG = AgentI18NFactory.getLogger(AgentMain.class); private final AgentMain agent; /** * The amount of time in milliseconds that this thread will sleep in between polling the server. */ private long interval = 1000L * 60 * 60; // 1 hour /** * Will be <code>true</code> when this thread is told to stop polling. Note that this does not * necessarily mean the thread is stopped, it just means this thread was told to stop. */ private volatile boolean toldToStop = false; public PrimaryServerSwitchoverThread(AgentMain agent) { super("RHQ Primary Server Switchover Thread"); setDaemon(true); this.agent = agent; } @Override public void run() { LOG.info(AgentI18NResourceKeys.PRIMARY_SERVER_SWITCHOVER_THREAD_STARTED); while (!isInterrupted() && !toldToStop) { try { // Note that if the agent is not sending or the failover list doesn't have any servers, // then we skip this time and wait some more. // However, it the agent is sending and we have a failover list, then we need to check // to see if the server we are currently talking to is the same as primary server, listed // at the top of the failover list. If not the same, we ask the agent to switch to that // server. ClientCommandSender sender = this.agent.getClientCommandSender(); if (sender.isSending()) { FailoverListComposite failoverList = this.agent.downloadServerFailoverList(); // ask the server for a new one // if the failover list doesn't have any servers, skip our poll and wait some more if (failoverList.size() > 0) { AgentConfiguration config = this.agent.getConfiguration(); String transport = config.getServerTransport(); String transportParams = config.getServerTransportParams(); String currentServerAddress = config.getServerBindAddress(); int currentServerPort = config.getServerBindPort(); ServerEntry primary = failoverList.get(0); // get the top of the list, aka primary server String primaryAddress = primary.address; int primaryPort = (SecurityUtil.isTransportSecure(transport)) ? primary.securePort : primary.port; if (!primaryAddress.equals(currentServerAddress) || primaryPort != currentServerPort) { LOG.info( AgentI18NResourceKeys.NOT_TALKING_TO_PRIMARY_SERVER, primaryAddress, primaryPort, currentServerAddress, currentServerPort); // create our own comm so we ping in an isolated client - don't reuse the sender's // comm for this RemoteCommunicator comm = this.agent.createServerRemoteCommunicator( transport, primaryAddress, primaryPort, transportParams); if (ping(comm)) { LOG.info(AgentI18NResourceKeys.PRIMARY_SERVER_UP, primaryAddress, primaryPort); failoverList.resetIndex(); // so the failover method call starts at the top this.agent.failoverToNewServer( sender .getRemoteCommunicator()); // note that we make sure we pass in the sender's // comm } else { LOG.info( AgentI18NResourceKeys.PRIMARY_SERVER_STILL_DOWN, primaryAddress, primaryPort); } } } } // to do sleep until its time to check again synchronized (this) { wait(interval); } } catch (InterruptedException ie) { break; // exiting } catch (Exception e) { LOG.warn(e, AgentI18NResourceKeys.PRIMARY_SERVER_SWITCHOVER_EXCEPTION, e); } } LOG.info(AgentI18NResourceKeys.PRIMARY_SERVER_SWITCHOVER_THREAD_STOPPED); return; } /** * Sets the time (in milliseconds) that this thread sleeps between checks. * * @param interval sleep time, in milliseconds (must not be less than 1000) */ public void setInterval(long interval) { this.interval = interval; } /** * Call this method when you want to stop this thread, which effectively stops it from checking * that the agent is pointing to its primary server. */ public void stopChecking() { toldToStop = true; interrupt(); // no need to notify, wait will exit } /** * Forces this thread to check now and switch to the primary if needed. If the thread is already * checking, this method does nothing. Effectively, this method wakes up this thread if its * sleeping during the {@link #setInterval(long) sleep interval}. */ public void checkNow() { synchronized (this) { notifyAll(); } } /** * Given the remote communicator (which isn't the one in the agent's command sender), this sends a * ping request to the remote endpoint and returns <code>true</code> if the remote endpoint is up. * * @param comm the communicator used to send the message * @return <code>true</code> if the communicator can send the message; <code>false</code> if the * remote endpoint is down * @throws Throwable */ private boolean ping(RemoteCommunicator comm) { boolean ok = true; // assume we can ping; on error, we'll set this to false IdentifyCommand id_cmd = new IdentifyCommand(); this.agent.getClientCommandSender().preprocessCommand(id_cmd); try { CommandResponse response = comm.sendWithoutCallbacks(id_cmd); // there is a special case when we might get a response back but it should be considered // "server down". // that is: when the server replies with a NotProcessedException response if (response.getException() instanceof NotProcessedException) { ok = false; } } catch (Throwable e) { ok = false; } return ok; } }