Java tutorial
/******************************************************************************* * Copyright (c) 2013 Christian Wiwie. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * Christian Wiwie - initial API and implementation ******************************************************************************/ package de.clusteval.serverclient; import java.io.IOException; import java.io.InputStream; import java.rmi.ConnectException; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import jline.console.ConsoleReader; import jline.console.completer.Completer; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import utils.ArraysExt; import utils.Pair; import utils.Triple; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.PatternLayout; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.FileAppender; import de.clusteval.run.RUN_STATUS; import file.FileUtils; import format.Formatter; /** * A backend client can give commands to the backend server (see * {@link IBackendServer}). * * @author Christian Wiwie * */ public class BackendClient extends Thread { protected static String VERSION; /** * This variable holds the command line options of the backend server. */ public static Options clientCLIOptions = new Options(); static { // read properties file with version number Properties prop = new Properties(); ClassLoader loader = Thread.currentThread().getContextClassLoader(); InputStream stream = loader.getResourceAsStream("client.date"); try { prop.load(stream); VERSION = "Jar built: " + prop.getProperty("buildtime") + "\nGit:\n\tCommit: " + prop.getProperty("gitrev") + "\n\tBranch: " + prop.getProperty("gitbranch") + "\n\tRepository: " + prop.getProperty("gitrepo"); } catch (IOException e) { e.printStackTrace(); } OptionBuilder.withArgName("level"); OptionBuilder.hasArg(); OptionBuilder.withDescription( "The verbosity this client should use during its execution. 0=ALL, 1=TRACE, 2=DEBUG, 3=INFO, 4=WARN, 5=ERROR, 6=OFF"); OptionBuilder.withType(Integer.class); Option option = OptionBuilder.create("logLevel"); clientCLIOptions.addOption(option); OptionBuilder.withDescription("Print this help and usage information"); Option optionHelp = OptionBuilder.create("help"); clientCLIOptions.addOption(optionHelp); OptionBuilder.withDescription("Print the version of this client"); Option optionVersion = OptionBuilder.create("version"); clientCLIOptions.addOption(optionVersion); // init valid command line options OptionBuilder.withArgName("ip"); OptionBuilder.hasArg(); OptionBuilder.withDescription("The ip address of the backend server to connect to."); option = OptionBuilder.create("ip"); clientCLIOptions.addOption(option); OptionBuilder.withArgName("port"); OptionBuilder.hasArg(); OptionBuilder.withDescription("The port number of the backend server to connect to."); option = OptionBuilder.create("port"); clientCLIOptions.addOption(option); OptionBuilder.withArgName("id"); OptionBuilder.hasArg(); OptionBuilder.withDescription("The client id for identification purposes of this client with the server."); option = OptionBuilder.create("clientId"); clientCLIOptions.addOption(option); OptionBuilder.withDescription("Queries the available datasets from the server."); option = OptionBuilder.create("getDataSets"); clientCLIOptions.addOption(option); OptionBuilder.withDescription("Queries the available programs from the server."); option = OptionBuilder.create("getPrograms"); clientCLIOptions.addOption(option); OptionBuilder.withDescription("Queries the available runs from the server."); option = OptionBuilder.create("getRuns"); clientCLIOptions.addOption(option); OptionBuilder.withDescription("Queries the available run resumes from the server."); option = OptionBuilder.create("getRunResumes"); clientCLIOptions.addOption(option); OptionBuilder.withDescription("Queries the available run results from the server."); option = OptionBuilder.create("getRunResults"); clientCLIOptions.addOption(option); OptionBuilder.withArgName("runName"); OptionBuilder.hasArg(); OptionBuilder.withDescription("Queries the status of a certain run"); option = OptionBuilder.create("getRunStatus"); clientCLIOptions.addOption(option); OptionBuilder.withArgName("runName"); OptionBuilder.hasArg(); OptionBuilder.withDescription("Queries the optimization status of a certain run"); option = OptionBuilder.create("getOptRunStatus"); clientCLIOptions.addOption(option); OptionBuilder.withDescription("Gets the enqueued runs and run resumes of the backend server"); option = OptionBuilder.create("getQueue"); clientCLIOptions.addOption(option); OptionBuilder.withDescription( "Gets the currently active threads and the corresponding iterations which they perform"); option = OptionBuilder.create("getActiveThreads"); clientCLIOptions.addOption(option); OptionBuilder.withArgName("runName"); OptionBuilder.hasArg(); OptionBuilder.withDescription("Performs a certain run (if not already running)"); option = OptionBuilder.create("performRun"); clientCLIOptions.addOption(option); OptionBuilder.withArgName("runName"); OptionBuilder.hasArg(); OptionBuilder.withDescription("Resumes a certain run that was started and terminated earlier."); option = OptionBuilder.create("resumeRun"); clientCLIOptions.addOption(option); OptionBuilder.withArgName("runName"); OptionBuilder.hasArg(); OptionBuilder.withDescription("Terminates a certain run that was started earlier."); option = OptionBuilder.create("terminateRun"); clientCLIOptions.addOption(option); OptionBuilder.withDescription("Shut down the framework."); option = OptionBuilder.create("shutdown"); clientCLIOptions.addOption(option); OptionBuilder.withDescription("The client will wait until all runs that this client started are finished."); option = OptionBuilder.create("waitForRuns"); clientCLIOptions.addOption(option); OptionBuilder.withArgName("generatorName"); OptionBuilder.hasArg(); OptionBuilder .withDescription("Generates a dataset using the generator " + "with the given name. Parameters for " + "this generator can be specified after " + "this command has been executed."); option = OptionBuilder.create("generateDataSet"); clientCLIOptions.addOption(option); OptionBuilder.withArgName("randomizerName"); OptionBuilder.hasArg(); OptionBuilder.withDescription( "Randomizes a dataconfig using the randomizer " + "with the given name. Parameters for " + "this randomizer can be specified after " + "this command has been executed."); option = OptionBuilder.create("randomizeDataConfig"); clientCLIOptions.addOption(option); } /** * The ip address on which this client should look for the server. */ private String ip; /** * The port on which this client should connect to the server. */ private String port; /** * The client id this client. Either the client id was specified in the * command line options, or the server told this client its id. * * <p> * The first scenario is helpful, if a client wants to connect multiple * times to the server. After a client reconnects to the server he is only * able to get the status of those runs, that belong to his client id. * Therefore in this case, the client needs to reconnect with the same id. */ private String clientId; /** * The server this client connected to. */ private IBackendServer server; private Logger log; /** * A wrapper object holding the parsed command line parameters of this * client. There are some connection parameters: * <ul> * <li><b>ip</b>: The ip address of the server</li> * <li><b>port</b>: The port of the server</li> * <li><b>clientId</b>: The id this client should use</li> * </ul> * There are other command line parameters, that cause the client to * terminate immediately after the command has been executed. * <ul> * <li><b>getDataSets</b>: This tells the client to get and print the * available datasets of the server.</li> * <li><b>getPrograms</b>: This tells the client to get and print the * available programs of the server.</li> * <li><b>getRuns</b>: This tells the client to get and print the available * runs of the server.</li> * <li><b>getRunResumes</b>: This tells the client to get and print all the * run result directories contained in the repository of this server. Those * run result directories can be resumed, if they were terminated before.</li> * <li><b>getRunResults</b>: This tells the client to get and print all the * run result directories contained in the repository of this server, that * contain a clusters subfolder and at least one *.complete file containing * results (can be slow if many run result folders are present).</li> * <li><b>getRunStatus</b>: This tells the client to get the status and * percentage (if) of a certain run.</li> * <li><b>performRun XXXX</b>: This tells the client to perform a run with a * certain name.</li> * <li><b>resumeRun XXXX</b>: This tells the client to resume a run * previously performed identified by its run result identifier.</li> * <li><b>terminateRun XXXX</b>: This tells the client to terminate the * execution of a run with a certain name.</li> * <li><b>shutdown</b>: This tells the client to shutdown the framework.</li> * <li><b>waitForRuns</b>: This option can be used together with * getRunStatus, in order to cause the client to wait until the run has * finished its execution.</li> * </ul> */ private CommandLine params; /** * This attribute holds only those arguments not yet parsed. Those can be * passed on to another commands like dataset generators. */ private String[] args; /** * Instantiates a new eval client. * * <p> * If no port is specified in the options, the default 1099 will be used. * * <p> * If no ip is specified, the localhost will be used. * * <p> * If no clientId is specified, the client will retrieve a new one from the * server. * * @param params * The command line parameters for this client (see * {@link #params}). * * @throws ConnectException * @throws ParseException */ public BackendClient(final String[] params) throws ConnectException, ParseException { super(); this.log = LoggerFactory.getLogger(this.getClass()); try { this.params = parseParams(params, true); } catch (ParseException e) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("ClustEvalFramework_Client.jar", "", clientCLIOptions, "Available commands are:", false); throw e; } /** * Keep a list of not parsed arguments. This has to be done, to maintain * the - of options passed, since they are not kept in the * CommandLine#args attribute. */ List<String> notParsedArgs = new ArrayList<String>(); for (String s : params) { String raw; if (s.startsWith("-")) raw = s.substring(1); else raw = s; if (this.params.getArgList().contains(raw) || this.params.getArgList().contains(s)) notParsedArgs.add(s); } this.args = notParsedArgs.toArray(new String[0]); if (this.params.hasOption("clientId")) this.clientId = this.params.getOptionValue("clientId"); if (this.params.hasOption("port")) this.port = this.params.getOptionValue("port"); else // default port this.port = "1099"; try { Registry registry; if (this.ip == null) registry = LocateRegistry.getRegistry(null, Integer.parseInt(this.port)); else registry = LocateRegistry.getRegistry(this.ip, Integer.parseInt(this.port)); server = (IBackendServer) registry.lookup("EvalServer"); if (this.clientId == null) this.clientId = server.getClientId(); this.log.debug("Connected to server using ClientId=" + this.clientId); } catch (ConnectException e) { this.log.error("Could not connect to server"); throw e; } catch (RemoteException e) { e.printStackTrace(); } catch (NotBoundException e) { e.printStackTrace(); } this.start(); } /** * A helper method for {@link #main(String[])} to parse the command line * parameters and put them into a wrapper object. * * @param params * The input command line parameters. * @param stopAtNonOptions * A boolean indicating, whether to throw an exception on unknown * options. * @return A wrapper object containing the parsed and valid parameters. * @throws ParseException */ private static CommandLine parseParams(String[] params, boolean stopAtNonOptions) throws ParseException { CommandLineParser parser = new PosixParser(); CommandLine cmd = parser.parse(clientCLIOptions, params, stopAtNonOptions); Level logLevel; if (cmd.hasOption("logLevel")) { switch (Integer.parseInt(cmd.getOptionValue("logLevel"))) { case 0: logLevel = Level.ALL; break; case 1: logLevel = Level.TRACE; break; case 2: logLevel = Level.DEBUG; break; case 3: logLevel = Level.INFO; break; case 4: logLevel = Level.WARN; break; case 5: logLevel = Level.ERROR; break; case 6: logLevel = Level.OFF; break; default: throw new ParseException("The logLevel argument requires one of the value of [0,1,2,3,4,5,6]"); } } else { logLevel = Level.INFO; } ((ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(logLevel); return cmd; } /* * (non-Javadoc) * * @see java.lang.Thread#run() */ @Override public void run() { boolean checkForRunStatus = false; boolean checkForOptRunStatus = false; try { if (params.hasOption("performRun")) { this.performRun(params.getOptionValue("performRun")); } if (params.hasOption("resumeRun")) { this.resumeRun(params.getOptionValue("resumeRun")); } if (params.hasOption("terminateRun")) { this.terminateRun(params.getOptionValue("terminateRun")); } if (params.hasOption("getRuns")) { System.out.println("Runs: " + this.getRuns()); } if (params.hasOption("getRunResumes")) { System.out.println("RunResumes: " + this.getRunResumes()); } if (params.hasOption("getQueue")) { System.out.println("Queue: " + this.getQueue()); } if (params.hasOption("getActiveThreads")) { Map<String, Triple<String, Integer, Long>> activeThreads = this.server.getActiveThreads(); if (activeThreads.isEmpty()) System.out.println("No active threads"); else { System.out.println("Active threads:"); System.out.format("%10s%20s%50s%30s%40s%10s%20s\n", "Thread #", "Thread", "Run", "ProgramConfig", "DataConfig", "Iteration", "Running time"); List<String> threadNames = new ArrayList<String>(activeThreads.keySet()); Collections.sort(threadNames); int i = 1; for (String t : threadNames) { Triple<String, Integer, Long> value = activeThreads.get(t); String[] split1 = value.getFirst().split(": "); String[] split2 = split1[1].split(","); switch (value.getSecond()) { case -1: System.out.format("%10d%20s%50s%30s%40s%10s%20s\n", i++, t, split1[0], split2[0], split2[1], "isoMDS", Formatter.formatMsToDuration( System.currentTimeMillis() - value.getThird(), false)); break; case -2: System.out.format("%10d%20s%50s%30s%40s%10s%20s\n", i++, t, split1[0], split2[0], split2[1], "PCA", Formatter.formatMsToDuration( System.currentTimeMillis() - value.getThird(), false)); break; default: System.out.format("%10d%20s%50s%30s%40s%10d%20s\n", i++, t, split1[0], split2[0], split2[1], value.getSecond(), Formatter.formatMsToDuration( System.currentTimeMillis() - value.getThird(), false)); } } } } if (params.hasOption("getRunResults")) { Map<Pair<String, String>, Map<String, Double>> result = this .getRunResults(params.getOptionValue("getRunResults")); boolean first = true; for (Pair<String, String> p : result.keySet()) { if (first) { for (String m : result.get(p).keySet()) { System.out.format("%-30s", ""); System.out.print("\t" + m); } System.out.println(); } System.out.format("%-30s", "(" + p.getFirst() + "," + p.getSecond() + ")"); for (String m : result.get(p).keySet()) { System.out.println("\t" + result.get(p).get(m)); } first = false; } } if (params.hasOption("getDataSets")) { System.out.println("DataSets: " + this.getDataSets()); } if (params.hasOption("getPrograms")) { System.out.println("Programs: " + this.getPrograms()); } if (params.hasOption("getRunStatus")) { checkForRunStatus = true; } if (params.hasOption("getOptRunStatus")) { checkForOptRunStatus = true; } if (params.hasOption("shutdown")) { this.shutdownFramework(); } if (params.hasOption("generateDataSet")) { String generatorName = params.getOptionValue("generateDataSet"); CommandLineParser parser = new PosixParser(); Options options = getOptionsForDataSetGenerator(generatorName); try { parser.parse(options, this.args); this.server.generateDataSet(generatorName, this.args); } catch (ParseException e1) { try { reader.println(); HelpFormatter formatter = new HelpFormatter(); formatter.setOptionComparator(new MyOptionComparator()); formatter.printHelp("generateDataSet " + generatorName, options, true); reader.println(); } catch (IOException e) { e.printStackTrace(); } } } if (params.hasOption("randomizeDataConfig")) { String randomizerName = params.getOptionValue("randomizeDataConfig"); CommandLineParser parser = new PosixParser(); Options options = getOptionsForDataRandomizer(randomizerName); try { parser.parse(options, this.args); this.server.randomizeDataConfig(randomizerName, this.args); } catch (ParseException e1) { try { reader.println(); HelpFormatter formatter = new HelpFormatter(); formatter.setOptionComparator(new MyOptionComparator()); formatter.printHelp("randomizeDataConfig " + randomizerName, options, true); reader.println(); } catch (IOException e) { e.printStackTrace(); } } } if (checkForRunStatus) { try { String runName = params.getOptionValue("getRunStatus"); Map<String, Pair<RUN_STATUS, Float>> status = null; if ((status = this.getMyRunStatus()) != null && status.size() > 0) { RUN_STATUS newStatus; Float percent; if (!status.containsKey(runName)) { log.info("No run with name " + runName + " running."); return; } newStatus = status.get(runName).getFirst(); percent = status.get(runName).getSecond(); System.out.println(); System.out.print("\r" + newStatus + " " + percent + "%"); } System.out.println(); } catch (ConnectException e2) { this.log.warn("The server terminated the connection..."); } catch (RemoteException e2) { e2.printStackTrace(); } } if (checkForOptRunStatus) { try { String runName = params.getOptionValue("getOptRunStatus"); // runId -> // ((Status,%),(ProgramConfig,DataConfig)->(QualityMeasure->(ParameterSet->Quality))) Map<String, Pair<Pair<RUN_STATUS, Float>, Map<Pair<String, String>, Map<String, Pair<Map<String, String>, String>>>>> optStatus = null; if ((optStatus = this.getMyOptimizationRunStatus()) != null && optStatus.size() > 0) { RUN_STATUS newStatus; Float percent; if (!optStatus.containsKey(runName)) { log.info("No run with name " + runName + " running."); return; } newStatus = optStatus.get(runName).getFirst().getFirst(); percent = optStatus.get(runName).getFirst().getSecond(); System.out.println(); System.out.println("\r Status:\t" + newStatus + " " + percent + "%"); Map<Pair<String, String>, Map<String, Pair<Map<String, String>, String>>> qualities = optStatus .get(runName).getSecond(); // print the quality measures; just take them from the // first pair of programConfig and dataConfig (runnable) String[] qualityMeasures = qualities.values().iterator().next().keySet() .toArray(new String[0]); Arrays.sort(qualityMeasures); int pos = 0; while (true) { boolean foundMeasure = false; for (String measure : qualityMeasures) { if (pos < measure.length()) { System.out.printf("\t%s", measure.charAt(pos)); foundMeasure = true; } else System.out.print("\t"); } System.out.println(); if (!foundMeasure) break; pos++; } // 06.06.2014: added sets to keep order when printing // the results Set<String> programConfigs = new HashSet<String>(); Set<String> dataConfigs = new HashSet<String>(); for (Pair<String, String> pcDcPair : qualities.keySet()) { programConfigs.add(pcDcPair.getFirst()); dataConfigs.add(pcDcPair.getSecond()); } for (String programConfig : programConfigs) { System.out.printf("%s:\n", programConfig); for (String dataConfig : dataConfigs) { System.out.printf("-- %s:\n", dataConfig); Pair<String, String> pcDcPair = Pair.getPair(programConfig, dataConfig); Map<String, Pair<Map<String, String>, String>> qualitiesPcDc = qualities .get(pcDcPair); for (String measure : qualityMeasures) { if (!qualitiesPcDc.containsKey(measure)) { System.out.print("\t"); continue; } String quality = qualitiesPcDc.get(measure).getSecond(); if (quality.equals("NT")) System.out.print("\tNT"); else { double qualityDouble = Double.valueOf(quality); if (Double.isInfinite(qualityDouble)) System.out.printf("\t%s%s", qualityDouble < 0 ? "-" : "", "Inf"); else System.out.printf("\t%.4f", qualityDouble); } } System.out.println(); } } } System.out.println(); } catch (ConnectException e2) { this.log.warn("The server terminated the connection..."); } catch (RemoteException e2) { e2.printStackTrace(); } } } catch (RemoteException e) { e.printStackTrace(); } } /** * This method will tell the server to shutdown the framework immediately (0 * timeout). */ public void shutdownFramework() { try { this.server.shutdown(this.clientId, 0); } catch (RemoteException e) { e.printStackTrace(); } } /** * This method retrieves the status of all the runs of this client. * * @return A map containing the status as well as the percentage (if) for * every run, this client has scheduled. * @throws RemoteException */ public Map<String, Pair<RUN_STATUS, Float>> getMyRunStatus() throws RemoteException { return server.getRunStatusForClientId(this.clientId); } /** * @return A collection with the names of all runs and run results that are * currently enqueued but not yet running. * @throws RemoteException */ public Collection<String> getQueue() throws RemoteException { return server.getQueue(); } /** * * @param uniqueRunIdentifier * The unique run identifier of a run result stored in the * corresponding directory of the repository. * @return The run results for the given unique run identifier. * @throws RemoteException */ public Map<Pair<String, String>, Map<String, Double>> getRunResults(final String uniqueRunIdentifier) throws RemoteException { return server.getRunResults(uniqueRunIdentifier); } /** * * @return A collection with the names of those run result directories * contained in the repository of this server, that contain a * clusters subfolder and at least one *.complete file containing * results (can be slow if many run result folders are present). * @throws RemoteException */ public Collection<String> getRunResults() throws RemoteException { return server.getRunResults(); } /** * @return A collection with the names of all run result directories * contained in the repository of this server. Those run result * directories can be resumed, if they were terminated before. * @throws RemoteException */ public Collection<String> getRunResumes() throws RemoteException { return server.getRunResumes(); } public Map<String, Pair<Pair<RUN_STATUS, Float>, Map<Pair<String, String>, Map<String, Pair<Map<String, String>, String>>>>> getMyOptimizationRunStatus() throws RemoteException { return server.getOptimizationRunStatusForClientId(this.clientId); } /** * @return The id of this client. */ public String getClientId() { return this.clientId; } /** * * @return A collection with all runs contained in the server's repository. * @throws RemoteException */ public Collection<String> getRuns() throws RemoteException { return server.getRuns(); } /** * @return A collection with all programs contained in the server's * repository. * @throws RemoteException */ public Collection<String> getPrograms() throws RemoteException { return server.getPrograms(); } /** * * @return A collection with the names of all dataset generators registered * at the repository of this server. * @throws RemoteException */ public Collection<String> getDataSetGenerators() throws RemoteException { return server.getDataSetGenerators(); } /** * * @param generatorName * The simple name of the class of the dataset generator. * @return A wrapper objects keeping all the options of the specified * dataset generator. * @throws RemoteException */ public Options getOptionsForDataSetGenerator(final String generatorName) throws RemoteException { return server.getOptionsForDataSetGenerator(generatorName); } /** * * @param randomizerName * The simple name of the class of the data randomizer. * @return A wrapper objects keeping all the options of the specified data * randomizer. * @throws RemoteException */ public Options getOptionsForDataRandomizer(final String randomizerName) throws RemoteException { return server.getOptionsForDataRandomizer(randomizerName); } /** * @return A collection with all datasets contained in the server's * repository. * @throws RemoteException * the remote exception */ public Collection<String> getDataSets() throws RemoteException { return server.getDataSets(); } /** * This method tells the server that this client wants to perform the * specified run. * * @param runId * The id of the run to be performed * @return true, if successful * @throws RemoteException */ public boolean performRun(final String runId) throws RemoteException { return server.performRun(this.clientId, runId); } /** * This method tells the server that this client wants to resume the run * that corresponds to the run results folder with the specified unique * identifier. * * @param uniqueRunIdentifier * The identifier of the run result that should be resumed. * @return True, if successful * @throws RemoteException */ public boolean resumeRun(final String uniqueRunIdentifier) throws RemoteException { return server.resumeRun(this.clientId, uniqueRunIdentifier); } /** * This method tells the server, that this client wants to terminate the * specified run. * * <p> * This method only succeeds, if the run has been started before and is * currently being performed. * * @param runId * The id of the run to be terminated. * @return true, if successful * @throws RemoteException * the remote exception */ public boolean terminateRun(final String runId) throws RemoteException { return server.terminateRun(this.clientId, runId); } /** * * @return A collection with the names of all data randomizers registered at * the repository of this server. * @throws RemoteException */ public Collection<String> getDataRandomizers() throws RemoteException { return server.getDataRandomizers(); } static ConsoleReader reader; /** * @param args * Command line parameters for the backend client (see * {@link #params}). * @throws IOException */ @SuppressWarnings("unused") public static void main(String[] args) throws IOException { try { CommandLine params = parseParams(args, false); if (params.hasOption("help")) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("clustevalClient", clientCLIOptions); System.exit(0); } if (params.hasOption("version")) { System.out.println(VERSION); System.exit(0); } initLogging(params); Logger log = LoggerFactory.getLogger(BackendClient.class); System.out.println("Starting clusteval client"); System.out.println(VERSION); System.out.println("========================="); // if command line arguments (except connection parameters) are // passed, we do not start a console Set<String> paramKeys = new HashSet<String>(); @SuppressWarnings("unchecked") Iterator<Option> it = params.iterator(); while (it.hasNext()) { paramKeys.add(it.next().getOpt()); } paramKeys.remove("ip"); paramKeys.remove("port"); paramKeys.remove("clientId"); if (paramKeys.size() > 0) { try { new BackendClient(args); } catch (Exception e) { } } else { String clientId; if (params.hasOption("clientId")) clientId = params.getOptionValue("clientId"); else // parse args because of possible connection parameters clientId = new BackendClient(args).clientId; reader = new ConsoleReader(); List<Completer> completers = new LinkedList<Completer>(); completers.add(new BackendClientCompleter(clientId, args)); String ip = params.hasOption("ip") ? params.getOptionValue("ip") : "localhost"; String port = params.hasOption("port") ? params.getOptionValue("port") : "1099"; setDefaultPromptAndCompleter(ip, port, completers); String line; while ((line = reader.readLine()) != null) { if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit")) { break; } boolean connectException = false; do { try { new BackendClient( ArraysExt.merge(args, ("-clientId " + clientId + " -" + line).split(" "))); connectException = false; } catch (ConnectException e) { e.printStackTrace(); connectException = true; } catch (Exception e) { log.warn(e.getMessage()); } Thread.sleep(1000); } while (connectException); // } } } } catch (ParseException e) { System.err.println("Parsing failed. Reason: " + e.getMessage()); HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("clustevalClient", "Invoking this client without any parameters will open a shell with tab-completion.", clientCLIOptions, "", true); } catch (Throwable t) { // t.printStackTrace(); } } protected static void setDefaultPromptAndCompleter(String ip, String port, Collection<Completer> newCompleters) { reader.setPrompt("ClusteringEvalFramework @" + ip + ":" + port + "> "); List<Completer> oldCompleters = new LinkedList<Completer>(reader.getCompleters()); for (Completer c : oldCompleters) reader.removeCompleter(c); for (Completer c : newCompleters) { reader.addCompleter(c); } } /** * This method is responsible for creating all the appender that are added * to the logger. * <p> * Three appenders are created: * <ul> * <li><b>ConsoleAppender</b>: Writes the logging output to the standard out * </li> * <li><b>FileAppender</b>: Writes the logging output as formatter text to * the file clustevalClient.log</li> * <li><b>FileAppender</b>: Writes the logging output in lilith binary * format to the file clustevalClient.lilith</li> * </ul> * * @param cmd * The command line parameters including possible options of * logging * @throws ParseException */ private static void initLogging(CommandLine cmd) throws ParseException { Logger log = LoggerFactory.getLogger(BackendClient.class); Level logLevel; if (cmd.hasOption("logLevel")) { switch (Integer.parseInt(cmd.getOptionValue("logLevel"))) { case 0: logLevel = Level.ALL; break; case 1: logLevel = Level.TRACE; break; case 2: logLevel = Level.DEBUG; break; case 3: logLevel = Level.INFO; break; case 4: logLevel = Level.WARN; break; case 5: logLevel = Level.ERROR; break; case 6: logLevel = Level.OFF; break; default: throw new ParseException("The logLevel argument requires one of the value of [0,1,2,3,4,5,6]"); } } else { logLevel = Level.INFO; } ch.qos.logback.classic.Logger logger = ((ch.qos.logback.classic.Logger) LoggerFactory .getLogger(Logger.ROOT_LOGGER_NAME)); logger.setLevel(logLevel); // file appender for clustevalServer.log plaintext file FileAppender<ILoggingEvent> fileApp = new FileAppender<ILoggingEvent>(); fileApp.setName("clientLogFile"); String logFilePath = FileUtils.buildPath(System.getProperty("user.dir"), "clustevalClient.log"); fileApp.setFile(logFilePath); fileApp.setAppend(true); fileApp.setContext((LoggerContext) LoggerFactory.getILoggerFactory()); fileApp.setEncoder(new PatternLayoutEncoder()); PatternLayout layout = new PatternLayout(); layout.setPattern("%date{dd MMM yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n"); layout.setContext((LoggerContext) LoggerFactory.getILoggerFactory()); layout.start(); fileApp.setLayout(layout); fileApp.start(); logger.addAppender(fileApp); // file appender for clustevalServer.lilith binary file // removed 30.01.2013 // FileAppender fileAppLilith = new FileAppender(); // fileAppLilith.setName("clientLogFileLilith"); // logFilePath = FileUtils.buildPath(System.getProperty("user.dir"), // "clustevalClient.lilith"); // fileAppLilith.setFile(logFilePath); // // fileAppLilith.setAppend(true); // fileAppLilith.setContext((LoggerContext) LoggerFactory // .getILoggerFactory()); // ClassicLilithEncoder encoder = new ClassicLilithEncoder(); // encoder.setIncludeCallerData(true); // fileAppLilith.setEncoder(encoder); // // fileAppLilith.start(); // logger.addAppender(fileAppLilith); log.debug("Using log level " + logLevel); } }