/* * $Id$ * * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.javatest.agent; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import com.sun.javatest.JavaTestSecurityManager; import com.sun.javatest.Status; /** * Start an agent, based on command line arguments. * No GUI is used. To create an agent with a GUI, see * AgentApplet or AgentFrame. * * @see Agent * **/ public class AgentMain { /** * This exception is used to report bad command line arguments. */ public static class BadArgs extends Exception { /** * Create a BadArgs exception. * @param msg A detail message about an error that has been found. */ public BadArgs(String msg) { this(new String[] { msg }); } /** * Create a BadArgs object. * @param msgs Detailed message about an error that has been found. */ public BadArgs(String[] msgs) { super(msgs[0]); this.msgs = msgs; } /** * Get the detail messages. * @return the messages given when this exception was created. */ public String[] getMessages() { return msgs; } private String[] msgs; } /** * This exception is used to report problems that occur while running. */ public static class Fault extends Exception { /** * Create a Fault exception. * @param msg A detail message about a fault that has occurred. */ public Fault(String msg) { this(new String[] { msg }); } /** * Create a Fault object. * @param msgs A detail message about a fault that has been found. */ public Fault(String[] msgs) { super(msgs[0]); this.msgs = msgs; } /** * Get the detail messages. * @return the messages given when this exception was created. */ public String[] getMessages() { return msgs; } private String[] msgs; } /** * Create and start an Agent, based on the supplied command line arguments. * * @param args The command line arguments * *
-help print a short summary of the command usage *
-usage print a short summary of the command usage *
-active set mode to be active *
-activeHost hostname set the host for active connections (implies -active) *
-activePort port set the port for active connections (implies -active) *
-passive set mode to be passive *
-passivePort port set the port for passive connections (implies -passive) *
-concurrency number set the maximum number of simultaneous connections *
-map file map file for translating arguments of incoming requests *
-trace trace the execution of the agent *
-observer classname add an observer to the agent that is used *
*/ public static void main(String[] args) { AgentMain m = new AgentMain(); m.runAndExit(args); } /** * Create and start an Agent, based on the supplied command line arguments. * This is for use by subtypes with their own main(String[]) entry point. * @param args the command line arguments for the run */ protected void runAndExit(String[] args) { // install our own permissive security manager, to prevent anyone else // installing a less permissive one; moan if it can't be installed. JavaTestSecurityManager.install(); if (Boolean.getBoolean("javatest.trace.printargs") && args != null && args.length > 0) { StringBuffer fullCmd = new StringBuffer(); StringBuffer incrementalCmd = new StringBuffer(); for (int i = 0; i < args.length; i++) { fullCmd.append(args[i]); fullCmd.append(" "); incrementalCmd.append("// "); incrementalCmd.append(args[i]); incrementalCmd.append("\n"); } // for System.out.println(fullCmd.toString().trim()); System.out.println(incrementalCmd.toString()); } int rc; try { run(args); rc = 0; } catch (BadArgs e) { System.err.println("Error: Bad arguments"); String[] msgs = e.getMessages(); for (int i = 0; i < msgs.length; i++) System.err.println(msgs[i]); System.err.println(); usage(System.err); rc = 1; } catch (Fault e) { String[] msgs = e.getMessages(); for (int i = 0; i < msgs.length; i++) System.err.println(msgs[i]); rc = 2; } catch (Throwable t) { t.printStackTrace(); rc = 3; } // If the JT security manager is installed, it won't allow a call of // System.exit unless we ask it nicely.. SecurityManager sc = System.getSecurityManager(); if (sc instanceof JavaTestSecurityManager) ((JavaTestSecurityManager) sc).setAllowExit(true); System.exit(rc); } /** * Run with the specified command-lines options. * This consists of the following steps: * * @param args An array of strings, typically provided via the command line * @throws AgentMain.BadArgs if a problem is found in the arguments provided * @throws AgentMain.Fault if a fault is found while running * @see #main * @see #decodeAllArgs * @see #validateArgs * @see #createAgent */ public void run(String[] args) throws BadArgs, Fault { decodeAllArgs(args); if (helpRequested) { usage(System.err); return; } validateArgs(); Agent agent = createAgent(); agent.addObserver(new ErrorObserver()); agent.run(); } /** * Decode an array of command line options, by calling decodeArg for * successive options in the array. * @param args the array of command line options * @throws AgentMain.BadArgs if a problem is found decoding the args * @throws AgentMain.Fault if the args can be decoded successfully but * if there is a problem in their interpretation (e.g invalid port number) */ protected void decodeAllArgs(String[] args) throws BadArgs, Fault { int i = 0; while (i < args.length) { int used = decodeArg(args, i); if (used == 0 ) throw new BadArgs("Unrecognised option: " + args[i]); i += used; } } /** * Decode the next command line option in an array of options. * @param args the array of command line options * @param index the position of the next option to be decoded * @return the number of elements consumed from the array * @throws AgentMain.BadArgs if a problem is found decoding the args * @throws AgentMain.Fault if the args can be decoded successfully but * if there is a problem in their interpretation (e.g invalid port number) */ protected int decodeArg(String[] args, int index) throws BadArgs, Fault { int i = index; try { if (args[i].equalsIgnoreCase("-active")) { mode = ACTIVE; modeCheck |= (1 << mode); return 1; } else if (args[i].equalsIgnoreCase("-passive")) { mode = PASSIVE; modeCheck |= (1 << mode); return 1; } else if (args[i].equalsIgnoreCase("-activeHost")) { mode = ACTIVE; modeCheck |= (1 << mode); activeHost = args[++i]; return 2; } else if (args[i].equalsIgnoreCase("-activePort")) { mode = ACTIVE; modeCheck |= (1 << mode); activePort = Integer.parseInt(args[++i]); return 2; } else if (args[i].equalsIgnoreCase("-passivePort")) { mode = PASSIVE; modeCheck |= (1 << mode); passivePort = Integer.parseInt(args[++i]); return 2; } else if (args[i].equalsIgnoreCase("-serialPort")) { mode = SERIAL; modeCheck |= (1 << mode); serialPort = args[++i]; return 2; } else if (args[i].equalsIgnoreCase("-concurrency")) { concurrency = Integer.parseInt(args[++i]); return 2; } else if (args[i].equalsIgnoreCase("-map")) { mapFile = args[++i]; return 2; } else if (args[i].equalsIgnoreCase("-mapArg")) { mappedArgs.put(args[++i], args[++i]); return 3; } else if (args[i].equalsIgnoreCase("-trace")) { tracing = true; return 1; } else if ("-observer".equalsIgnoreCase(args[i]) && i < args.length - 1) { if (observerClassName != null) throw new BadArgs("duplicate use of -observer"); observerClassName = args[++i]; return 2; } else if (args[i].equalsIgnoreCase("-help") || args[i].equalsIgnoreCase("-usage") ) { helpRequested = true; return (args.length - index); // consume remaining args } else return 0; // unrecognized } catch (ArrayIndexOutOfBoundsException e) { throw new BadArgs("Missing argument for " + args[args.length - 1]); } catch (NumberFormatException e) { throw new BadArgs("Number expected: " + args[i]); } } /** * Validate the values decoded by decodeAllArgs. * @throws AgentMain.BadArgs if a problem is found validating the args that * is likely caused by a misunderstanding of the command line options or syntax * @throws AgentMain.Fault if there is some other problem with the args, such * as a bad host name or a port not being available for use */ protected void validateArgs() throws BadArgs, Fault { if (modeCheck == 0) throw new BadArgs("No connection options given"); if (modeCheck != (1 << mode)) throw new BadArgs("Conflicting options for connection to JT Harness harness"); switch (mode) { case ACTIVE: if (activeHost == null || activeHost.length() == 0) throw new BadArgs("No active host specified"); if (activePort <= 0) throw new BadArgs("No active port specified"); break; case SERIAL: if (serialPort == null) throw new BadArgs("No serial port specified"); } if (!Agent.isValidConcurrency(concurrency)) { throw new BadArgs("Bad value for concurrency: " + concurrency); } } /** * Create a connection factory based on the values decoded by decodeAllArgs * Normally called from createAgent. * @return a connection factory based on the values decoded by decodeAllArgs * @throws AgentMain.Fault if there is a problem createing the factory */ protected ConnectionFactory createConnectionFactory() throws Fault { String s = AgentMain.class.getName(); String pkg = s.substring(0, s.lastIndexOf('.')); switch (mode) { case ACTIVE: try { Class c = Class.forName(pkg + ".ActiveConnectionFactory"); Constructor m = c.getConstructor(new Class[] {String.class, int.class}); Object[] args = { activeHost, new Integer(activePort) }; return (ConnectionFactory)(m.newInstance(args)); } catch (Throwable e) { Throwable t = unwrapInvocationTargetException(e); String[] msgs = { "Error occurred while trying to start an active agent", t.toString(), "Are the java.net classes available?" }; throw new Fault(msgs); } case PASSIVE: try { Class c = Class.forName(pkg + ".PassiveConnectionFactory"); Constructor m = c.getConstructor(new Class[] {int.class, int.class}); Object[] args = { new Integer(passivePort), new Integer(concurrency + 1) }; return (ConnectionFactory)(m.newInstance(args)); } catch (Throwable e) { Throwable t = unwrapInvocationTargetException(e); if (t instanceof IOException) throw new Fault("Cannot create socket on port " + passivePort); else { String[] msgs = { "Error occurred while trying to start a passive agent", t.toString(), "Are the java.net classes available?" }; throw new Fault(msgs); } } case SERIAL: try { Class c = Class.forName(pkg + ".SerialPortConnectionFactory"); Constructor m = c.getConstructor(new Class[] {String.class, String.class, int.class}); Object[] args = {serialPort, Agent.productName, new Integer(10*1000)}; return (ConnectionFactory)(m.newInstance(args)); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof IllegalArgumentException || t.getClass().getName().equals("gnu.io.NoSuchPortException")) { throw new Fault(serialPort + " is not a valid port"); } else { String[] msgs = { "Error occurred while trying to access the communication ports", t.toString(), "Is the gnu.io extension installed?" }; throw new Fault(msgs); } } catch (Throwable e) { String[] msgs = { "Error occurred while trying to access the communication ports", e.toString(), "Is the gnu.io extension installed?" }; throw new Fault(msgs); } default: throw new Error("unexpected mode"); } } /** * Create an agent based on the command line options previously decoded. * createConnectionFactory() is used to create the connection factory required * by the agent. * @return an agent based on the values decoded by decodeAllArgs * @throws AgentMain.Fault if there is a problem createing the agent */ protected Agent createAgent() throws Fault { ConnectionFactory cf = createConnectionFactory(); Agent agent = new Agent(cf, concurrency); agent.setTracing(tracing); if (observerClassName != null) { try { Class observerClass = Class.forName(observerClassName); Agent.Observer observer = (Agent.Observer)(observerClass.newInstance()); agent.addObserver(observer); } catch (ClassCastException e) { throw new Fault("observer is not of type " + Agent.Observer.class.getName() + ": " + observerClassName); } catch (ClassNotFoundException e) { throw new Fault("cannot find observer class: " + observerClassName); } catch (IllegalAccessException e) { throw new Fault("problem instantiating observer: " + e); } catch (InstantiationException e) { throw new Fault("problem instantiating observer: " + e); } } // for now, we only read a map file if one is explicitly specified; // in JDK 1.1, it might be nice to check if <>.jtm exists // or something like that. In JDK 1.0.2, getting the HOSTNAME is // problematic, because INetAddress fails the verifier. if (mapFile != null) { try { agent.setMap(Map.readFileOrURL(mapFile)); } catch (IOException e) { String[] msgs = {"Problem reading map file", e.toString()}; throw new Fault(msgs); } } else if (mappedArgs.size() > 0) { agent.setMap(new Map(mappedArgs)); } Integer delay = Integer.getInteger("agent.retry.delay"); if (delay != null) agent.setRetryDelay(delay.intValue()); return agent; } /** * Display the set of options recognized by main(String[] args). * @param out a stream to which to write the information */ public void usage(PrintStream out) { String className = getClass().getName(); out.println("Usage:"); out.println(" java " + className + " [options]"); out.println(" -help print this message"); out.println(" -usage print this message"); out.println(" -active set mode to be active"); out.println(" -activeHost host set the host for active connections (implies -active)"); out.println(" -activePort port set the port for active connections (implies -active)"); out.println(" -passive set mode to be passive"); out.println(" -passivePort port set the port for passive connections (implies -passive)"); out.println(" -serialPort port set the port for serial port connections"); out.println(" -map file map file for translating arguments of incoming requests"); out.println(" -mapArg from to map \"from\" arg to \"to\" arg for incoming requests"); out.println(" -concurrency num set the maximum number of simultaneous connections"); out.println(" -trace trace the execution of the agent"); out.println(" -observer class add an observer to the agent"); } /** * Unwrap an InvocationTargetException. * @param t the exception to be unwrapped * @return the argument's target exception if the argument is an * InvocationTargetException; otherwise the argument itself is returned. */ protected static Throwable unwrapInvocationTargetException(Throwable t) { if (t instanceof InvocationTargetException) return ((InvocationTargetException) t).getTargetException(); else return t; } private boolean helpRequested = false; private int mode = 0; private int modeCheck = 0; private String activeHost = null; private int activePort = Agent.defaultActivePort; private int passivePort = Agent.defaultPassivePort; private String serialPort = null; private int concurrency = 1; private String mapFile = null; private java.util.Map mappedArgs = new HashMap(); private String observerClassName; private boolean tracing; private static final int ACTIVE = 1; private static final int PASSIVE = 2; private static final int SERIAL = 3; private static final int max(int a, int b) { return (a > b ? a : b); } private static class ErrorObserver implements Agent.Observer { ErrorObserver() { try { connectExceptionClass = Class.forName("java.net.ConnectException", true, ClassLoader.getSystemClassLoader()); } catch (Throwable t) { // ignore } try { unknownHostExceptionClass = Class.forName("java.net.UnknownHostException", true, ClassLoader.getSystemClassLoader()); } catch (Throwable t) { // ignore } } public void started(Agent agent) { } public void errorOpeningConnection(Agent agent, Exception e) { if (connectExceptionClass != null && connectExceptionClass.isInstance(e)) { long now = System.currentTimeMillis(); if (lastNotRespondMsgTime + lastNotRespondMsgInterval < now) { System.err.println("host not responding: " + e.getMessage()); lastNotRespondMsgTime = now; } } else if (unknownHostExceptionClass != null && unknownHostExceptionClass.isInstance(e)) System.err.println("unknown host: " + e.getMessage()); else System.err.println("error connecting to host: " + e); } private long lastNotRespondMsgTime = 0; private int lastNotRespondMsgInterval = max(Integer.getInteger("notResponding.message.interval", 60).intValue(), 10)*1000; public void finished(Agent agent) { } public void openedConnection(Agent agent, Connection c) { } public void execTest(Agent agent, Connection c, String tag, String className, String[] args) { } public void execCommand(Agent agent, Connection c, String tag, String className, String[] args) { } public void execMain(Agent agent, Connection c, String tag, String className, String[] args) { } public void result(Agent agent, Connection c, Status result) { } public void exception(Agent agent, Connection c, Throwable e) { } public void completed(Agent agent, Connection c) { } private Class connectExceptionClass; private Class unknownHostExceptionClass; } }