UDP.java :  » UnTagged » destroyallhumans » hypermedia » net » Android Open Source

Android Open Source » UnTagged » destroyallhumans 
destroyallhumans » hypermedia » net » UDP.java
/**
 * (./) UDP.java v0.2 06/01/26
 * (by) Douglas Edric Stanley & Cousot Stphane
 * (cc) some right reserved
 *
 * Part of the Processing Libraries project, for the Atelier Hypermedia, art 
 * school of Aix-en-Provence, and for the Processing community of course.
 * - require Java version 1.4 or later -
 * -> http://hypermedia.loeil.org/processing/
 * -> http://www.processing.org/
 
 * THIS LIBRARY IS RELEASED UNDER A CREATIVE COMMONS LICENSE BY.
 * -> http://creativecommons.org/licenses/by/2.5/
 */


package hypermedia.net;

import java.net.*;
import java.io.*;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.text.SimpleDateFormat;

import processing.core.*;

/** 
 * Create and manage unicast, broadcast or multicast socket to send and receive
 * datagram packets over the network.
 * <p>
 * The socket type is define at his initialyzation by the passed IP address. 
 * To reach a host (interface) within a network, you need to specified the kind 
 * of address:
 * <ul>
 * <li>An <b>unicast address</b> refer to a unique host within a subnet.</li>
 * <li>A <b>broadcast address</b> allow you to call every host within a subnet.
 * </li>
 * <li>A <b>multicast address</b> allows to call a specific group of hosts within 
 * the subnet. A multicast group is specified by a IP address in the range 
 * 224.0.0.0 (reserved, should not be used) to 
 * 239.255.255.255 inclusive, and by a standard UDP port number.
 * <br />
 * <small>notes: the complete reference of special multicast addresses should be
 * found in the latest available version of the "Assigned Numbers RFC"
 * </small></li>
 * </ul>
 * A packet sent to a unicast or broadcast address is only delivered to the 
 * host identified by that address. To the contrary, when packet is send to a 
 * multicast address, all interfaces identified by that address receive the data
 * .
 * <p>
 * To perform actions on receive and/or timeout events, you must implement 
 * specific method(s) in your code. This method will be automatically called
 * when the socket receive incoming data or a timeout event. By default, the
 * "receive handler" is typically <code>receive(byte[] data)</code> but you can
 * retrieve more informations about the datagram packet, see 
 * {@link UDP#setReceiveHandler(String name)} for more informations. In the same
 * logic, the default "timeout handler" is explicitly <code>timeout()</code>.
 * <p>
 * <small>
 * note: currently applets are not allowed to use multicast sockets
 * </small>
 *
 * @version 0.1
 * @author Cousot Stphane - stef@ubaa.net
 * @author Douglas Edric Stanley - http://www.abstractmachine.net/
 */
public class UDP implements Runnable {
  
  
  // the current unicast/multicast datagram socket
  DatagramSocket ucSocket    = null;
  MulticastSocket mcSocket  = null;
  
  boolean log      = false;  // enable/disable output log
  boolean listen    = false;  // true, if the socket waits for packets
  int timeout      = 0;    // reception timeout > 0=infinite timeout
  int size      = 65507;  // the socket buffer size in bytes
  InetAddress group  = null;    // the multicast's group address to join
  
  // the reception Thread > wait automatically for incoming datagram packets
  // without blocking the current Thread.
  Thread thread    = null;
  
  // the parent object (could be an application, a componant, etc...)
  Object owner    = null;
  
  // the default "receive handler" and "timeout handler" methods name.
  // these methods must be implemented (by the owner) to be called 
  // automatically when the socket receive incoming datas or a timeout event
  String receiveHandler    = "receive";
  String timeoutHandler    = "timeout";
  
  // the log "header" to be set for debugging. Because log is disable by 
  // default, this value is automatically replaced by the principal socket 
  // settings when a new instance of UDP is created.
  String header    = "";
  
  ///////////////////////////////// fields ///////////////////////////////
  
  /**
   * The default socket buffer length in bytes.
   */
  public static final int BUFFER_SIZE = 65507;
  
  
  ///////////////////////////// constructors ////////////////////////////
  
  /**
   * Create a new datagram socket and binds it to an available port and every 
   * address on the local host machine.
   *
   * @param owner  the target object to be call by the receive handler
   */
  public UDP( Object owner ) {
    this( owner, 0 );
  }
  
  /**
   * Create a new datagram socket and binds it to the specified port on the 
   * local host machine.
   * <p>
   * Pass <code>zero</code> as port number, will let the system choose an 
   * available port.
   *
   * @param owner  the target object to be call by the receive handler
   * @param port  local port to bind
   */
  public UDP( Object owner, int port ) {
    this( owner, port, null );
  }
  
  /**
   * Create a new datagram socket and binds it to the specified port on the  
   * specified local address or multicast group address.
   * <p>
   * Pass <code>zero</code> as port number, will let the system choose an 
   * available port. The absence of an address, explicitly <code>null</code> 
   * as IP address will assign the socket to the Unspecified Address (Also 
   * called anylocal or wildcard address). To set up the socket as multicast 
   * socket, pass the group address to be joined. If this address is not a 
   * valid multicast address, a broadcast socket will be created by default.
   *
   * @param owner  the target object to be call by the receive handler
   * @param port  local port to bind
   * @param ip  host address or group address
   */
  public UDP( Object owner, int port, String ip ) {
    
    this.owner = owner;
    
    // register this object to the PApplet, 
    // if it's used with Processing
    try {
      if ( owner instanceof PApplet ) 
        ((PApplet)owner).registerDispose( this );
    }
    catch( NoClassDefFoundError e ) {;}
    
    // open a new socket to the specified port/address
    // and join the group if the multicast socket is required
    try {
      
      InetAddress addr = InetAddress.getByName(ip);
      InetAddress host = (ip==null) ? (InetAddress)null: addr;
      
      if ( !addr.isMulticastAddress() ) {
        ucSocket = new DatagramSocket( port, host );  // as broadcast
        log( "bound socket to host:"+address()+", port: "+port() );
      }
      else {              
        mcSocket = new MulticastSocket( port );      // as multicast
        mcSocket.joinGroup( addr );
        this.group = addr;
        log( "bound multicast socket to host:"+address()+
           ", port: "+port()+", group:"+group );
      }
    }
    catch( IOException e ) { 
      // caught SocketException & UnknownHostException
      error( "opening socket failed!"+
           "\n\t> address:"+ip+", port:"+port+" [group:"+group+"]"+
           "\n\t> "+e.getMessage()
        ); 
    }
    catch( IllegalArgumentException e ) { 
      error( "opening socket failed!"+
           "\n\t> bad arguments: "+e.getMessage()
           );
    }
    catch( SecurityException e ) {
      error( (isMulticast()?"could not joined the group":"warning")+
          "\n\t> "+e.getMessage()  );
    }
    
  }
  
  /////////////////////////////// methods ///////////////////////////////
  
  /** 
   * Close the socket. This method is automatically called by Processing when 
   * the PApplet shuts down.
   *
   * @see UDP#close()
   */
  public void dispose() {
    close();
  }
  
  /**
   * Close the actuel datagram socket and all associate resources.
   */
  public void close() {
    if ( isClosed() ) return;
    
    int port  = port();
    String ip  = address();
    
    // stop listening if needed
    //listen( false );
    
    // close the socket
    try {
      if ( isMulticast() ) {
        if ( group!=null ) {
          mcSocket.leaveGroup( group );
          log( "leave group < address:"+group+" >" );
        }
        mcSocket.close();
        mcSocket = null;
      }
      else {
        ucSocket.close();
        ucSocket = null;
      }
    }
    catch( IOException e ) {
      error( "Error while closing the socket!\n\t> " + e.getMessage() ); 
    }
    catch( SecurityException e ) {;}
    finally {
      log( "close socket < port:"+port+", address:"+ip+" >\n" );
    }
  }
  
  /**
   * Returns whether the current socket is closed or not.
   * @return boolean
   **/
  public boolean isClosed() {
    if ( isMulticast() ) return mcSocket==null ? true : mcSocket.isClosed();
    return ucSocket==null ? true : ucSocket.isClosed();
  }
  
  /**
   * Return the actual socket's local port, or -1 if the socket is closed.
   * @return int
   */
  public int port() {
    if ( isClosed() ) return -1;
    return isMulticast()? mcSocket.getLocalPort() : ucSocket.getLocalPort();
  }
  
  /**
   * Return the actual socket's local address, or <code>null</code> if the 
   * address correspond to any local address.
   *
   * @return String
   */
  public String address() {
    if ( isClosed() ) return null;
    
    InetAddress laddr = isMulticast() ? mcSocket.getLocalAddress(): 
                      ucSocket.getLocalAddress();
    return laddr.isAnyLocalAddress() ? null : laddr.getHostAddress();
  }
  
  /**
   * Send message to the current socket. Explicitly, send message to the 
   * multicast group/port or to itself.
   *
   * @param message  the message to be send
   *
   * @see  UDP#send(String message, String ip)
   * @see  UDP#send(String message, String ip, int port)
   *
   * @return boolean
   */
  public boolean send( String message ) {
    return send( message.getBytes() );
  }
  
  /**
   * Send data to the current socket. Explicitly, send data to the multicast 
   * group/port or to itself.
   *
   * @param buffer  data to be send
   *
   * @see  UDP#send(byte[] data, String ip)
   * @see  UDP#send(byte[] data, String ip, int port)
   *
   * @return boolean
   */
  public boolean send( byte[] buffer ) {
    // probably if the group could not be joined for a security reason
    if ( isMulticast() && group==null ) return false;
    
    String ip = isMulticast() ? group.getHostAddress() : address();
    return send( buffer, ip, port() );
  }
  
  /**
   * Send message to the requested IP address, to the current socket port.
   *
   * @param message  the message to be send
   * @param ip    the destination host's IP address
   *
   * @see  UDP#send(String message)
   * @see  UDP#send(String message, String ip, int port)
   *
   * @return boolean
   */
  public boolean send( String message, String ip ) {
    return send( message.getBytes(), ip );
  }
  
  /**
   * Send data to the requested IP address, to the current socket port.
   *
   * @param buffer  data to be send
   * @param ip    the destination host's IP address
   *
   * @see  UDP#send(byte[] buffer)
   * @see  UDP#send(byte[] buffer, String ip, int port)
   *
   * @return boolean
   */
  public boolean send( byte[] buffer, String ip ) {
    return send( buffer, ip, port() );
  }
  
  /**
   * Send message to the requested IP address and port.
   * <p>
   * A <code>null</code> IP address will assign the packet address to the 
   * local host. Use this method to send message to another application by
   * passing <code>null</code> as the destination address.
   *
   * @param message  the message to be send
   * @param ip    the destination host's IP address
   * @param port    the destination host's port
   *
   * @see  UDP#send(String message)
   * @see  UDP#send(String message, String ip)
   *
   * @return boolean
   */
  public boolean send( String message, String ip, int port ) {
    return send( message.getBytes(), ip, port );
  }
  
  /**
   * Send data to the requested IP address and port.
   * <p>
   * A <code>null</code> IP address will assign the packet address to the 
   * local host. Use this method to send data to another application by
   * passing <code>null</code> as the destination address.
   *
   * @param buffer  data to be send
   * @param ip    the destination host's IP address
   * @param port    the destination host's port
   *
   * @see  UDP#send(byte[] buffer, String ip)
   * @see  UDP#send(byte[] buffer, String ip, int port)
   *
   * @return boolean
   */
  public boolean send( byte[] buffer, String ip, int port ) {
    
    boolean success  = false;
    DatagramPacket pa = null;
    
    try {
      
      pa  = new DatagramPacket( buffer, buffer.length, InetAddress.getByName(ip), port );
        
      // send
      if ( isMulticast() ) mcSocket.send( pa );
      else ucSocket.send( pa );
      
      success = true;
      log( "send packet -> address:"+pa.getAddress()+
         ", port:"+ pa.getPort() +
        ", length: "+ pa.getLength()
         );
    }
    catch( IOException e ) {
      error( "could not send message!"+
           "\t\n> port:"+port+
          ", ip:"+ip+
          ", buffer size: "+size+
          ", packet length: "+pa.getLength()+
           "\t\n> "+e.getMessage()
          );
    }
    finally{ return success; }
  }
  
  /**
   * Set the maximum size of the packet that can be sent or receive on the 
   * current socket. This value must be greater than 0, otherwise the buffer 
   * size is set to the his default value.
   * <p>
   * return <code>true</code> if the new buffer value have been correctly set, 
   * <code>false</code> otherwise.
   * <p>
   * <i>note : this method has no effect if the socket is listening. To define
   * a new buffer size, call this method before implementing a new buffer in 
   * memory. Explicitly before calling a receive methods.</i>
   *
   * @param size  the buffer size value in bytes or n<=0
   * @return boolean
   * @see UDP#getBuffer()
   */
  public boolean setBuffer( int size ) {
    boolean done = false;
    
    // do nothing if listening (block the thread otherwise)
    if ( isListening() ) return done;
    
    try {
      // set the SO_SNDBUF and SO_RCVBUF value
      if ( isMulticast() ) {
        mcSocket.setSendBufferSize( size>0 ? size : BUFFER_SIZE );
        mcSocket.setReceiveBufferSize( size>0 ? size : BUFFER_SIZE );
      }
      else {
        ucSocket.setSendBufferSize( size>0 ? size : BUFFER_SIZE );
        ucSocket.setReceiveBufferSize( size>0 ? size : BUFFER_SIZE );
      }
      // set the current buffer size
      this.size = size>0 ? size : BUFFER_SIZE;
      done = true;
    }
    catch( SocketException e ) {
      error( "could not set the buffer!"+
           "\n> "+e.getMessage()
          );
    }
    finally{ return done; }
  }
  
  /**
   * Return the actual socket buffer length
   * @return int
   * @see UDP#setBuffer(int size)
   */
  public int getBuffer() {
    return size;
  }
  
  /**
   * Returns whether the socket wait for incoming data or not.
   * @return boolean
   */
  public boolean isListening() {
    return listen;
  }
  
    
  /**
   * Start/stop waiting constantly for incoming data.
   *
   * @param on  the required listening status.
   *
   * @see UDP#listen()
   * @see UDP#listen(int millis)
   * @see UDP#setReceiveHandler(String name)
   */
  public void listen( boolean on ) {
    
    listen  = on;
    timeout  = 0;
    
    // start
    if ( on && thread==null && !isClosed() ) {
      thread = new Thread( this );
      thread.start();
    }
    // stop
    if ( !on && thread!=null  ) { 
      send( new byte[0] ); // unblock the thread with a dummy message
      thread.interrupt();
      thread = null;
    }
  }
  
  /**
   * Set the socket reception timeout and wait one time for incoming data. 
   * If the timeout period occured, the owner timeout() method is 
   * automatically called.
   *
   * @param millis  the required timeout value in milliseconds.
   *
   * @see UDP#listen()
   * @see UDP#listen(boolean on)
   */
  public void listen( int millis ) {
    if ( isClosed() ) return;
    
    listen  = true;
    timeout = millis;
    
    // unblock the thread with a dummy message, if already listening
    if ( thread!=null ) send( new byte[0] );
    if ( thread==null ) {
      thread = new Thread( this );
      thread.start();
    }
  }
  
  /**
   * Wait for incoming data, and call the appropriate handlers each time a 
   * message is received. If the owner's class own the appropriate target 
   * handler, this method send it the receive message as byte[], the sender 
   * IP address and port.
   * <p>
   * This method force the current <code>Thread</code> to be ceased for a 
   * temporary period. If you prefer listening without blocking the current 
   * thread, use the {@link UDP#listen(int millis)} or 
   * {@link UDP#listen(boolean on)} method instead.
   *
   * @see UDP#listen()
   * @see UDP#listen(boolean on)
   * @see UDP#setReceiveHandler(String name)
   */
  public void listen() {
    try {
      
      byte[] buffer    = new byte[ size ];
      DatagramPacket pa  = new DatagramPacket(buffer,buffer.length);
      
      // wait
      if ( isMulticast() ) {
        mcSocket.setSoTimeout( timeout );
        mcSocket.receive( pa );  // <-- block the Thread
      }
      else {
        ucSocket.setSoTimeout( timeout );
        ucSocket.receive( pa ); // <-- block
      }

      
      log( "receive packet <- from "+pa.getAddress()+
         ", port:"+ pa.getPort() +
         ", length: "+ pa.getLength()
         );
      
    
      // get the required data only (not all the buffer)
      // and send it to the appropriate target handler, if needed
      if ( pa.getLength()!=0 ) {
        
        byte[] data = new byte[ pa.getLength() ];
        System.arraycopy( pa.getData(), 0, data, 0, data.length );
        
        try { 
          // try with one argument first > byte[]
          callReceiveHandler( data );
        }
        catch( NoSuchMethodException e ) {
          // try with many argument > byte[], String, int
          callReceiveHandler( data, 
                    pa.getAddress().getHostAddress(), 
                    pa.getPort()
                    );
        }
      }
    }
    catch( NullPointerException e ) {
      // *socket=null from the close() method;
      listen = false;
      thread = null;
    }
    catch( IOException e ) {
      
      listen = false;
      thread = null;
      
      if ( e instanceof SocketTimeoutException ) callTimeoutHandler();
      else {
         // do not print "Socket closed" error 
        // if the method close() has been called
        if ( ucSocket!=null && mcSocket!=null )
          error( "listen failed!\n\t> "+e.getMessage() );
      }
    }
  }
  
  /**
   * Wait for incoming datagram packets. This method is called automaticlly,
   * you do not need to call it.
   */
  public void run() {
    while ( listen ) listen();
  }
  
  /**
   * Register the target's receive handler.
   * <p>
   * By default, this method name is "receive" with one argument 
   * representing the received data as <code>byte[]</code>. For more 
   * flexibility, you can change this method with another useful method by 
   * passing his name. You could get more informations by implementing two 
   * additional arguments, a <code>String</code> representing the sender IP 
   * address and a <code>int</code> representing the sender port :
   * <p><blockquote><pre>
   * void myCustomReceiveHandler(byte[] message, String ip, int port) {
   *  // do something here...
   * }
   * </blockquote></pre>
   *
   * @param name  the receive handler name
   * @see UDP#setTimeoutHandler(String name)
   */
  public void setReceiveHandler( String name ) {
    this.receiveHandler = name;
  }
  
  /**
   * Call the default receive target handler method.
   *
   * @param data  the data to be passed
   * @throws NoSuchMethodException
   */
  private void callReceiveHandler( byte[] data ) 
  throws NoSuchMethodException {
    
    Class[] types;    // arguments class types
    Object[] values;  // arguments values
    Method method;
    
    try {
      types  = new Class[]{ data.getClass() };
      values  = new Object[]{ data };
      method  = owner.getClass().getMethod(receiveHandler, types);
      method.invoke( owner, values );
    }
    catch( IllegalAccessException e )    { error(e.getMessage()); }
    catch( InvocationTargetException e )  { e.printStackTrace(); }
  }

  /**
   * Call the receive target handler implemented with the optional arguments.
   *
   * @param data    the data to be passed
   * @param ip    the IP adress to be passed
   * @param port    the port number to be passed
   */
  private void callReceiveHandler( byte[] data, String ip, int port ) {
    
    Class[] types;    // arguments class types
    Object[] values;  // arguments values
    Method method;
    
    try {
      types  = new Class[]{  data.getClass(),
                  ip.getClass(), 
                  Integer.TYPE
                };
      values  = new Object[]{ data, 
                  ip, 
                  new Integer(port)
                };
      method  = owner.getClass().getMethod(receiveHandler, types);
      method.invoke( owner, values );
    }
    catch( NoSuchMethodException e )    {;}
    catch( IllegalAccessException e )    { error(e.getMessage()); }
    catch( InvocationTargetException e )  { e.printStackTrace(); }
  }
  
  /**
   * Register the target's timeout handler. By default, this method name is 
   * "timeout" with no argument.
   *
   * @param name  the timeout handler name
   * @see UDP#setReceiveHandler(String name)
   */
  public void setTimeoutHandler( String name ) {
    this.timeoutHandler = name;
  }
  
  /**
   * Call the timeout target method when the socket received a timeout event.
   * The method name to be implemented in your code is <code>timeout()</code>.
   */
  private void callTimeoutHandler() {
    try {
      Method m = owner.getClass().getDeclaredMethod(timeoutHandler, null);
      m.invoke( owner, null );
    }
    catch( NoSuchMethodException e )    {;}
    catch( IllegalAccessException e )    { error(e.getMessage()); }
    catch( InvocationTargetException e )  { e.printStackTrace(); }
  }
  
  /**
   * Returns whether the opened datagram socket is a multicast socket or not.
   * @return boolean
   */
  public boolean isMulticast() {
    return ( mcSocket!=null );
  }
  
  /**
   * Returns whether the multicast socket is joined to a group address.
   * @return boolean
   */
  public boolean isJoined() {
    return ( group!=null );
  }
  
  /**
   * Returns whether the opened socket send broadcast message socket or not.
   * @return boolean
   */
  public boolean isBroadcast() {
    boolean result = false;
    try {
      result = (ucSocket==null) ? false : ucSocket.getBroadcast();
    }
    catch( SocketException e ) { error( e.getMessage() ); }
    finally { return result; }
  }
  
  /** 
   * Enables or disables the ability of the current process to send broadcast
   * messages.
   * @return boolean
   */
  public boolean broadcast( boolean on ) {
    boolean done = false;
    try {
      if ( ucSocket!=null ) {
        ucSocket.setBroadcast( on );
        done = isBroadcast();
      }
    }
    catch( SocketException e ) { error( e.getMessage() ); }
    finally { return done; }
  }
  
  /**
   * Enable or disable the multicast socket loopback mode. By default loopback
   * is enable.
   * <br>
   * Setting loopback to false means this multicast socket does not want to 
   * receive the data that it sends to the multicast group.
   *
   * @param on  local loopback of multicast datagrams
   */
  public void loopback( boolean on ) {
    try {
      if ( isMulticast() ) mcSocket.setLoopbackMode( !on );
    }
    catch( SocketException e ) { 
      error( "could not set the loopback mode!\n\t>"+e.getMessage() ); 
    }
  }
  
  /**
   * Returns whether the multicast socket loopback mode is enable or not.
   * @return boolean
   */
  public boolean isLoopback() {
    try {
      if ( isMulticast() && !isClosed() ) 
        return !mcSocket.getLoopbackMode();
    }
    catch( SocketException e ) { 
      error( "could not get the loopback mode!\n\t> "+e.getMessage() ); 
    }
    return false;
  }
  
  /**
   * Control the life-time of a datagram in the network for multicast packets 
   * in order to indicates the scope of the multicasts (ie how far the packet 
   * will travel).
   * <p>
   * The TTL value must be in range of 0 to 255 inclusive. The larger the 
   * number, the farther the multicast packets will travel (by convention):
   * <blockquote><pre>
   * 0  -> restricted to the same host
   * 1  -> restricted to the same subnet (default)
   * &lt;32  -> restricted to the same site
   * &lt;64  -> restricted to the same region
   * &lt;128  -> restricted to the same continent
   * &lt;255  -> no restriction
   * </blockquote></pre>
   * The default value is 1, meaning that the datagram will not go beyond the 
   * local subnet.
      * <p>
   * return <code>true</code> if no error occured.
   *
   * @param ttl the "Time to Live" value
   * @return boolean
   * @see UDP#getTimeToLive()
   */
  public boolean setTimeToLive( int ttl ) {
    try {
      if ( isMulticast() && !isClosed() ) mcSocket.setTimeToLive( ttl );
      return true;
    }
    catch( IOException e ) { 
      error( "setting the default \"Time to Live\" value failed!"+
           "\n\t> "+e.getMessage() ); 
    }
    catch( IllegalArgumentException e ) {
      error( "\"Time to Live\" value must be in the range of 0-255" ); 
    }
    return false;
  }
  
  /**
   * Return the "Time to Live" value or -1 if an error occurred (or if 
   * the current socket is not a multicast socket).
   *
   * @return int
   * @see UDP#setTimeToLive(int ttl)
   */
  public int getTimeToLive() {
    try {
      if ( isMulticast() && !isClosed() ) 
        return mcSocket.getTimeToLive();
    }
    catch( IOException e ) { 
      error( "could not retrieve the current time-to-live value!"+
           "\n\t> "+ e.getMessage() ); 
    }
    return -1;
  }
  
  /**
   * Enable or disable output process log.
   */
  public void log( boolean on ) {
    log = on;
  }
  
  /**
   * Output message to the standard output stream.
   * @param out  the output message
   */
  private void log( String out ) {
    
    Date date = new Date();
    
    // define the "header" to retrieve at least the principal socket
    // informations : the host/port where the socket is bound.
    if ( !log && header.equals("") )
      header = "-- UDP session started at "+date+" --\n-- "+out+" --\n";
    
    // print out
    if ( log ) {
      
      String pattern  = "yy-MM-dd HH:mm:ss.S Z";
      String sdf    = new SimpleDateFormat(pattern).format( date );
      System.out.println( header+"["+sdf+"] "+out );
      header = ""; // forget header
    }
  }
  
  /**
   * Output error messages to the standard error stream.
   * @param err the error string
   */
  private void error( String err ) {
    System.err.println( err );
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.