Package cz.matfyz.aai.fantom.server

Source Code of cz.matfyz.aai.fantom.server.Client

/*
   This file is part of Fantom.

    Fantom is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Fantom 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 Fantom.  If not, see <http://www.gnu.org/licenses/>.
*/
package cz.matfyz.aai.fantom.server;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.log4j.Logger;

import cz.matfyz.aai.fantom.message.ClientType;
import cz.matfyz.aai.fantom.message.Message;
import cz.matfyz.aai.fantom.message.MessageReady;
import cz.matfyz.aai.fantom.message.MessagingClientBase;

/**
* Maintains the client process and provides method for messaging
* with the client.
*/
public class Client extends MessagingClientBase {
 
  /**
   * The logger used by this class.
   */
  protected Logger logger = Logger.getLogger(Server.LOGGER_NAME);
 
  /**
   * The tokenized command line used to start the client process.
   */
  private String[] commandLine;
 
  /**
   * The name of the client.
   */
  private String clientName;
 
  /**
   * Caches the full name of the client, as soon as it is known (after initializing
   * the communication with the client).
   *
   * @see #getFullClientName()
   */
  private String fullName;
 
  /**
   * The process of the client.
   */
  private Process clientProcess;
 
  /**
   * The type of the client (detective/phantom).
   */
  private ClientType clientType;
 
  /**
   * Output logger used to redirect standard error output of the
   * client process to a file.
   */
  private OutputLogger clientErrLogger;
 
  /**
   * The name of the current run.
   */
  private String runName;
 
  /**
   * Specifies whether the messages between the server and the clients
   * should be logged to a file.
   */
  private boolean logMessages;
 
  /**
   * Ends the client process.
   * @throws ServerException if there is a problem with ending
   *             the client process.
   */
  public void endProcess() throws ServerException {
    if(clientProcess == null)
      return;
    try {
      closeStreams();
     
      int exit = clientProcess.waitFor();
      if(exit != 0)
        System.err.format("Client '%s' terminated with exit code %d\n", getFullClientName(), exit);
    }
    catch (IOException e) {
      throw new ServerException("Could not close the streams", e);
    }
    catch (InterruptedException e) {
      throw new ServerException("Interrupted", e);
    }
  }
 
  /**
   * Returns (the name of) the file that contains (or should contain) the logs
   * from the client. In the current run. The name of the file is determined
   * using the name of the current run, and the type of the client.
   *
   * @return the file object that points to the file that is used to store logs
   *       from the client in the current run.
   * @throws IOException
   */
  public File getClientLogFile() throws IOException {
    String fileName = String.format("%s-%s.log", runName, getClientType().toString());
    return new File(fileName);
  }
 
  /**
   * Returns the name of the file that contains the logs of messages going between
   * the server and the client in current run. The name of the file is determined
   * using the name of the run and the type of the client.
   *
   * @return the file object that points to the file that is used to store message
   *       logs.
   * @throws IOException
   */
  public File getClientMessageLogFile() throws IOException {
    String fileName = String.format("%s-%s.message.log", runName, getClientType().toString());
    return new File(fileName);
  }
 
  /**
   * Returns the name of the client (as sent by the client in the beginning of the
   * client-server communication).
   * @return the name of the client.
   */
  public String getClientName() {
    return this.clientName;
  }
 
  /**
   * Returns the type of the client.
   * @return the type of the client.
   */
  public ClientType getClientType() {
    return this.clientType;
  }
 
  /**
   * Returns a full name of the client. This consists of the name of the client and
   * the command-line arguments used to run the client.
   * @return the full name of the client.
   */
  public String getFullClientName() {
    if(fullName == null) {
      StringBuilder sb = new StringBuilder();
      if(clientName != null && !clientName.isEmpty()) {
        sb.append(clientName);
        sb.append(" : ");
      }
      for(String clPart : commandLine) {
        if(sb.length() > 0)
          sb.append(' ');
        sb.append(clPart);
      }
     
      // Do not store the full name, unless the name reported by the client is
      // known - this method gets called both before and after the client reports
      // its name.
      if(clientName == null || clientName.isEmpty())
        return sb.toString();
     
      fullName = sb.toString();
    }
    return fullName;
  }
 
  /**
   * Kills the client process and closes the associated streams. This should
   * only be used, if the game ends in an unexpected way.
   */
  public void killProcess() {
    if(clientProcess == null)
      return;
    clientProcess.destroy();
    try {
      closeStreams();
    }
    catch(IOException e) {
     
    }
   
    try {
      if(clientErrLogger != null)
        clientErrLogger.wait(1000);
    }
    catch(Exception e) {
     
    }
  }
 
  /**
   * Starts the client process and reads the name of the client.
   *
   * @throws ServerException if there is a problem with starting the
   *         client process, or with communication with the client.
   */
  public void startProcess() throws ServerException {
    try {
      logger.debug("Starting client process");
      clientProcess = Runtime.getRuntime().exec(commandLine);
    } catch (IOException e) {
      throw new ClientException("The client process was not started", this);
    }

    try {
      logger.debug("Redirecting input and output for the clients");
      FileOutputStream clientMessageLogger = null;
      if(logMessages)
        clientMessageLogger = new FileOutputStream(getClientMessageLogFile());
      setStreams(clientProcess.getInputStream(), clientProcess.getOutputStream(), clientMessageLogger);

      logger.debug("Redirecting standard error output of the client");
      clientErrLogger = new OutputLogger(clientProcess.getErrorStream(), getClientLogFile());
      clientErrLogger.start();
    } catch (IOException e) {
      throw new ServerException("Initialization of the communication with the client failed");
    }

    logger.debug("Waiting for 'ready' from the client");
    Message msg = receiveMessage(null);
    if(!(msg instanceof MessageReady))
      throw new ProtocolException("The 'ready' message was expected", this);

    MessageReady msgReady = (MessageReady)msg;
    this.clientName = msgReady.getClientName();
    logger.debug("Client connected: " + this.clientName);
   
    if(this.clientType != msgReady.getClientType())
      throw new ClientException("Expected client type " + this.clientType
          + " does not match the real client type " + msgReady.getClientType(), this);
  }

  /**
   * Initializes a new client.
   *
   * @param commandLine the tokenized command line used to start the client.
   */
  public Client(String[] commandLine, ClientType clientType, String runName, boolean logMessages) {
    if(commandLine == null || commandLine.length == 0)
      throw new IllegalArgumentException("The command for running the client was not provided");
    if(runName == null)
      throw new IllegalArgumentException("No run name was specified");
    if(clientType == null)
      throw new IllegalArgumentException("No client type was specified");
    this.commandLine = commandLine;
    this.clientType = clientType;
    this.runName = runName;
    this.logMessages = logMessages;
  }
}
TOP

Related Classes of cz.matfyz.aai.fantom.server.Client

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.