diff options
-rw-r--r-- | java/netsvcs/Logger/LogMessageReceiver.java | 43 | ||||
-rw-r--r-- | java/netsvcs/Logger/LogRecord.java | 185 | ||||
-rw-r--r-- | java/netsvcs/Logger/LoggerTest.java | 112 | ||||
-rw-r--r-- | java/netsvcs/Logger/ServerLoggingAcceptor.java | 157 | ||||
-rw-r--r-- | java/netsvcs/Logger/ServerLoggingHandler.java | 112 |
5 files changed, 609 insertions, 0 deletions
diff --git a/java/netsvcs/Logger/LogMessageReceiver.java b/java/netsvcs/Logger/LogMessageReceiver.java new file mode 100644 index 00000000000..a2e6df9fba5 --- /dev/null +++ b/java/netsvcs/Logger/LogMessageReceiver.java @@ -0,0 +1,43 @@ +/************************************************* + * + * = PACKAGE + * netsvcs.Logger + * + * = FILENAME + * LogMessageReceiver.java + * + *@author Everett Anderson + * + *************************************************/ +package netsvcs.Logger; + +import java.lang.*; +import java.io.*; +import netsvcs.Logger.LogRecord; + +/** + * + * <p><h2>DESCRIPTION</h2> + * + * <blockquote> + * The LogMessageReceiver removes the code that handles a log message from + * the logging service acceptor. The DefaultLMR simply calls the LogRecord's + * print method. Other implementations of this interface can be built and + * given to the ServerLoggingAcceptor to change the result. + * + * @see netsvcs.Logger.ServerLoggingAcceptor, netsvcs.Logger.LogRecord + */ +public interface LogMessageReceiver +{ + public void logRecord (String hostname, + LogRecord record); +}; + +class DefaultLMR implements LogMessageReceiver +{ + public void logRecord (String hostname, + LogRecord record) + { + record.print(hostname, true, System.err); + } +} diff --git a/java/netsvcs/Logger/LogRecord.java b/java/netsvcs/Logger/LogRecord.java new file mode 100644 index 00000000000..721341cb252 --- /dev/null +++ b/java/netsvcs/Logger/LogRecord.java @@ -0,0 +1,185 @@ +/************************************************* + * + * = PACKAGE + * netsvcs.Logger + * + * = FILENAME + * LogRecord.java + * + *@author Chris Cleeland, Everett Anderson + * + *************************************************/ +package netsvcs.Logger; + +import java.util.Date; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.io.PrintStream; +import java.io.IOException; +import JACE.OS.*; + +/** + * + * <p><h2>DESCRIPTION</h2> + * + * <blockquote> + * Communicates logging information. Compatible with the C++ ACE + * ACE_Log_Record class. + * + */ +public class LogRecord +{ + final public int MAXLOGMSGLEN = 4 * 1024; + + private int type_; + private int length_; + private long msec_; + private int pid_; + private byte[] msgData_; + private final static int numIntMembers = 5; + private final static int sizeofIntInBytes = 4; + + /** + * Create a default instance. + */ + public LogRecord() + { + this(0, (int)new Date().getTime(), 0); + } + + /** + * Create a LogRecord. This is the designated initializer. + * @param priority a numeric specification of the priority (ascending) + * @param milliseconds time attached to the log entry in Unix <pre>time_t</pre> format + * @param pid the process ID; not currently used + */ + public LogRecord(int priority, + long milliseconds, + int pid) + { + type(priority); + timeStamp(milliseconds); + length(0); + pid(pid); + } + + /** + * Conversion to string. Only includes the <pre>msgData_</pre> member. + */ + public String toString() + { + return new String(msgData_); + } + + /** + * Place a textual representation of the record on a PrintStream. + * @param hostname name of the host generating this record + * @param verbose if <b>true</b>, print information in the form, (give example) + * @param ps A PrintStream instance to which the output should go. + * @see PrintStream,String + */ + public void print(String hostname, + boolean verbose, + PrintStream ps) + { + String toprint; + if (verbose) + { + Date now = new Date(this.timeStamp()); + + /* 01234567890123456789012345 */ + /* Wed Oct 18 14:25:36 1989n0 */ + toprint = now.toString().substring(4) + "@" + + hostname + "@" + pid_ + "@" + type_ + "@" + + this.toString(); + } + else + { + toprint = this.toString(); + } + ps.println(toprint); + } + + /** + * Streaming methods + */ + public void streamInFrom(DataInputStream dis) throws IOException + { + // Order here must match layout order in the C++ class. + // This, of course, is VERY fragile, and ought not be used as + // a model for anything except how NOT to do anything. + type(dis.readInt()); + length(dis.readInt()); + this.timeStamp((long)dis.readInt() * 1000); + + // Skip smaller time resolution info since we're lucky if Java's + // timer can handle more than millisecond precision, anyway + dis.skipBytes(4); + + pid(dis.readInt()); + + // Does readFully() allocate space for the buffer? Either + // way, we won't have memory leaks :-) + int dataLength = (int) (length_ - numIntMembers * sizeofIntInBytes); + msgData_ = new byte[dataLength]; + dis.readFully(msgData_, 0, dataLength); + } + + public void streamOutTo(DataOutputStream dos) throws IOException + { + dos.writeInt(length()); + dos.writeInt(type()); + dos.writeInt(length()); + dos.writeInt((int)(this.msec_ / 1000)); + dos.writeInt(0); + dos.writeInt(pid()); + + dos.write(msgData_); + } + + /** + * Accessor methods + */ + public int type() { return type_; } + public void type(int t) { type_ = t; } + + public int length() { return length_; } + public void length(int l) { length_ = l; } + private void setLen(int msgLen) + { length(msgLen + numIntMembers * sizeofIntInBytes); } + + public long timeStamp() { return this.msec_; } + public void timeStamp(long msec){ this.msec_ = msec; } + + public int pid() { return pid_; } + public void pid(int p) { pid_ = p; } + + public byte[] msgData() { return msgData_; } + public void msgData(byte[] m) + { + int size = m.length; + + if (size > MAXLOGMSGLEN) + size = MAXLOGMSGLEN; + + this.msgData_ = new byte[size]; + + System.arraycopy(m, 0, msgData_, 0, size); + + setLen(size); + } + + public void msgData(String m) + { + byte temp[] = m.getBytes(); + if (temp.length > MAXLOGMSGLEN) { + this.msgData_ = new byte[MAXLOGMSGLEN]; + + System.arraycopy(temp, 0, msgData_, 0, MAXLOGMSGLEN); + } else + this.msgData_ = temp; + + setLen(msgData_.length); + } +}; + diff --git a/java/netsvcs/Logger/LoggerTest.java b/java/netsvcs/Logger/LoggerTest.java new file mode 100644 index 00000000000..52a952ce5ee --- /dev/null +++ b/java/netsvcs/Logger/LoggerTest.java @@ -0,0 +1,112 @@ +/************************************************* + * + * = FILENAME + * LoggerTest.java + * + *@author Everett Anderson + * + *************************************************/ +import JACE.SOCK_SAP.*; +import java.io.*; +import java.net.*; +import JACE.OS.*; +import netsvcs.Logger.LogRecord; + +/** + * + * <p><h2>DESCRIPTION</h2> + * + * <blockquote> + * This is an example/test log client very similar to the direct_logging client of + * C++ ACE. The server logging service should correctly receive messages from both + * of these examples. + * </blockquote> + * + * @see netsvcs.Logger.ServerLoggingAcceptor, netsvcs.Logger.LogRecord + */ +public class LoggerTest { + + // Command line: <port> <hostname> + // + // Creates a "hello world" log message and sends it to the server logging service. + // If no command line arguments are given, it uses ACE.DEFAULT_SERVER_PORT, and the + // current machine for the service location. + // + public static void main(String args[]) + { + int port = args.length > 0 ? (new Integer(args[0])).intValue() : ACE.DEFAULT_SERVER_PORT; + + SOCKStream cli_stream = new SOCKStream(); + INETAddr remote_addr; + String host; + + try { + + host = args.length > 1 ? args[1] : InetAddress.getLocalHost().getHostName(); + + remote_addr = new INETAddr(port, host) +; + } catch (UnknownHostException uhe) { + ACE.ERROR("UnknownHostException " + uhe); + return; + } + + System.out.println("Connecting to " + host + " on port " + port); + + SOCKConnector con = new SOCKConnector(); + + try { + + // Connect to the service + con.connect(cli_stream, remote_addr); + + } catch (SocketException se) { + + ACE.ERROR("Socket Exception " + se); + return; + + } catch (IOException ie) { + + ACE.ERROR("IOException " + ie); + return; + } + + + // Send a message with priority 4, the current time, + // and 0 for the process ID. + LogRecord record = new LogRecord(4, + System.currentTimeMillis(), + 0); + + // Set the message + record.msgData("hello world"); + + try { + + // Get a transmission system from the socket + OutputStream os = cli_stream.socket().getOutputStream(); + DataOutputStream dos = new DataOutputStream(os); + + // Send it + record.streamOutTo(dos); + + // Close the socket + cli_stream.close(); + + } catch (IOException ie) { + + ACE.ERROR("IOException, loop: " + ie); + return; + } + } +}; + + + + + + + + + + diff --git a/java/netsvcs/Logger/ServerLoggingAcceptor.java b/java/netsvcs/Logger/ServerLoggingAcceptor.java new file mode 100644 index 00000000000..c68c089a0f7 --- /dev/null +++ b/java/netsvcs/Logger/ServerLoggingAcceptor.java @@ -0,0 +1,157 @@ +/************************************************* + * + * = PACKAGE + * netsvcs.Logger + * + * = FILENAME + * ServerLoggingAcceptor.java + * + *@author Chris Cleeland, Everett Anderson + * + *************************************************/ +package netsvcs.Logger; + +import JACE.SOCK_SAP.*; +import JACE.Connection.*; +import netsvcs.Logger.*; +import JACE.OS.*; +import java.io.*; +import java.net.*; +import JACE.Misc.*; + +/** + * + * <p><h2>DESCRIPTION</h2> + * + * <blockquote> + * Acceptor: Listens on a specified port and launches ServerLoggingHandlers + * in response to requests. A LogMessageReceiver can be specified on the + * command line to change the way the logging service processes messages. + * + * @see netsvcs.Logger.ServerLoggingHandler, netsvcs.Logger.LogMessageReceiver + */ +public class ServerLoggingAcceptor extends Acceptor implements Runnable +{ + // Main function so the service can be started without the service + // configurator. + public static void main (String [] args) + { + ServerLoggingAcceptor sla = new ServerLoggingAcceptor(); + + sla.init(args); + } + + // Called by the service configurator, receives the command line and + // launches its own thread. + public int init (String [] args) + { + this.parseArgs(args); + + // *** should contain choices like what the default is + // in an options singleton? + if (this.receiver_ == null) + this.receiver_ = new DefaultLMR(); + + new Thread (this).start(); + return 0; + } + + // Specify what LogMessageReceiver to use + public void setLMR(LogMessageReceiver receiver) + { + this.receiver_ = receiver; + } + + // Accessor for the LogMessageReceiver + public LogMessageReceiver getLMR () + { + return this.receiver_; + } + + // Create a new ServerLoggingHandler + protected SvcHandler makeSvcHandler () + throws InstantiationException, IllegalAccessException + { + // ** could contain this choice in a singleton, too + return new netsvcs.Logger.ServerLoggingHandler (this.receiver_); + } + + // Run forever accepting new connections + public void run () + { + try { + + this.open (this.port_); + while (true) + this.accept(); // blocks + + } catch (SocketException e) + { + ACE.ERROR ("Socket Exception: " + e); + } + catch (InstantiationException e) + { + ACE.ERROR (e); + } + catch (IllegalAccessException e) + { + ACE.ERROR (e); + } + catch (IOException e) + { + ACE.ERROR (e); + } + + ACE.ERROR("ServerLoggingAcceptor has exited"); + } + + // Process the command line + protected void parseArgs (String args[]) + { + String s; + GetOpt opt = new GetOpt (args, "p:r:"); + for (int c; (c = opt.next ()) != -1; ) + { + switch (c) + { + case 'p': + s = opt.optarg (); + this.port_ = (new Integer (s)).intValue (); + break; + case 'r': + // Load the LMR with the given name + s = new String(opt.optarg ()); + Class LMRfactory; + try { + LMRfactory = Class.forName(s); + + receiver_ = (LogMessageReceiver)LMRfactory.newInstance(); + + } catch (ClassNotFoundException e) { + ACE.ERROR("Unable to find LMR factory: " + e); + } catch (InstantiationException e) { + ACE.ERROR("Creating LMR: " + e); + } catch (IllegalAccessException e) { + ACE.ERROR("Creating LMR: " + e); + } + // Any of the above exceptions will result in just using the + // default LMR + break; + default: + ACE.ERROR ("Unknown argument: " + c); + ACE.ERROR ("Valid args: -p <port> -r <LogMessageReceiver name>"); + break; + } + } + } + + // port should be in options singleton ** + private int port_ = ACE.DEFAULT_SERVER_PORT; + private LogMessageReceiver receiver_ = null; +}; + + + + + + diff --git a/java/netsvcs/Logger/ServerLoggingHandler.java b/java/netsvcs/Logger/ServerLoggingHandler.java new file mode 100644 index 00000000000..c8a364526cc --- /dev/null +++ b/java/netsvcs/Logger/ServerLoggingHandler.java @@ -0,0 +1,112 @@ +/************************************************* + * + * = PACKAGE + * netsvcs.Logger + * + * = FILENAME + * ServerLoggingHandler.java + * + *@author Chris Cleeland, Everett Anderson + * + *************************************************/ +package netsvcs.Logger; + +import JACE.SOCK_SAP.*; +import JACE.Connection.*; +import JACE.OS.*; +import java.util.*; +import java.io.*; + +/** + * + * <p><h2>DESCRIPTION</h2> + * + * <blockquote> + * Created by ServerLoggingAcceptor every time a client connects. This reads + * a logging statement passes it to the LogMessageReceiver for processing. + * </blockquote> + * + * @see netsvcs.Logger.ServerLoggingAcceptor + */ +public class ServerLoggingHandler extends SvcHandler +{ + // Processes log messages + private LogMessageReceiver receiver_; + + // Constructor + public ServerLoggingHandler (LogMessageReceiver receiver) + { + super(); + this.receiver_ = receiver; + } + + // Start this handler in its own thread + public int open(Object obj) + { + new Thread (this).start(); + return 0; + } + + // Accessor: get the host name of the connected client + protected String hostName () + { + return new String(this.stream_.socket().getInetAddress().getHostName()); + } + + // Receive input from the client, and send it to the LMR + public void run() + { + DataInputStream dis = (DataInputStream) this.stream_.inputStream(); + + for (;;) + { + // Messages arrive in the following format: + // o 4 byte length (network format) + // o message, in ACE.LogRecord format + // + // Hey! We need exception catching in here too! + try + { + // Reconstitute a log message from the wire + LogRecord rec = new LogRecord(); + + // We don't really need this, because + // the object already knows how to + // extract itself properly. However, + // in order to interoperate with the + // C++ version, this must be extracted. + // Plus, it makes a convenient way to + // check everything. + int length = dis.readInt(); + + rec.streamInFrom(dis); + + if (rec.length() == length) + { + // Give the record to the log processor + this.receiver_.logRecord(this.hostName(), + rec); + } + else + { + ACE.ERROR(Thread.currentThread().getName() + ": Length error"); + } + } + catch (EOFException eof) + { + try { + this.stream_.close(); + } catch (IOException n) { } + + return; + } + catch (IOException ioe) + { + ACE.ERROR(Thread.currentThread().getName() + + ": " + + ioe); + } + } + } +}; + |