Summary
I am finishing up the remote LogManager monitoring facilities in my http://logman.jini.org project and I'd like to share some interesting details about the implemenation.
Advertisement
A Remote Log Monitoring Facility with Jini
I created an implementation of this concept using a factory model
as described in my earler blog. That implementation is visible in
the project source tree at http://logman.jini.org now.
There are some interesting classes that might be useful for
other applications. I needed a streaming connection for the
log stream, so I created a RemoteSocketStream class that is
deserialized as an InputStream. Its
readObject(ObjectInputStream) method will
make the connection back to the remote socket, and pass a
Uuid cookie back to id with the server.
The method signature that I added to the RemoteHandler interface
is shown below.
public HandlerControl listenToLogger(
FormatterFactory fact ) throws IOException, UnsupportedOperationException;
Initially, the remote Handler is attached to the top level of the
logger name space. This will cause all LogRecords of the
appropriate Level in the
tree of Loggers to be sent to the client.
The Implementation...
The HandlerControl
interface returned by listenToLogger(FormatterFactory)
provides the ability to adjust the
logging stream. It is defined as follows.
public interface HandlerControl {
/** Get the logging data stream. */
public InputStream getInputStream() throws IOException;
/** Set the log level of the remote {@link java.util.logging.Handler} */
public void setLogLevel( Level l ) throws IOException;
/** Add a {@link java.util.logging.Logger} to the set of loggers being monitored */
public void addLogger( String name ) throws IOException;
/** Remove a {@link java.util.logging.Logger} from the set of loggers being monitored */
public void removeLogger( String name ) throws IOException;
/** Close the {@link java.util.logging.Logger} stream */
public void close() throws IOException;
}
There are of course many different ways to implement the actions of this
interface. I want to talk about what I did because
I think the implementation shows how mobile code and
custom RPC mechanisms can be used to exploit connectivity between a
client and service.
My implementation of the RemoteHandler.listenToLogger(FormatterFactory)
method returns a RemoteSocketStream subclass called
HandlerSocketStream.
The super class, RemoteSocketStream, is a serializable
java.io.InputStream subclass. The
deserialization code in
RemoteSocketStream.readObject(ObjectInputStream)
makes the Socket connection back to the server so that
data can start streaming out to the remote client. The client need
only start reading from the InputStream to see the logging stream.
Exploiting a Socket Connection
The HandlerControl methods are layered on top of that Socket stream.
Since the path to the server is already in place, it is not necessary to use
an RMI interface for controlling the server end. Instead, I chose to send simple lines
of text to implement the actions of the HandlerControl interface.
The remote socket reader, at the server,
reads the lines of text comming back from the client
and processes lines formatted as indicated below.
+
Add the indicated logger to the list of monitored loggers.
-
Remove the indicated logger from the list of monitored loggers.
=
Set the remote Handler Level to the indicated level.
Result is that the HandlerControl implementation does not need to be an
RMI proxy. Instead, it is a Serializable class that uses
a socket connection back to the service to implement the methods of the interface.
Solving the Reverse Path Problem
In the world of the Internet, and other places where Network Address Translation and Firewalling exists, there is not always a bidirectional, fully routable path between computers. Typically, a client computer will not be accessible from a server for connection initiation. Thus, in cases like this where a stream of data, generated at the server, needs to flow to the client, the implementation needs to be able to deal with this issue.
The solution developed here solves this problem by having the client make the connection to the server. This allows the route from the server to the client to not be an issue.
When you develop services that you need to remotely adminster in remote
networks, this type of solution can simplify several issues.
Summing it all Up
When you need a stream from a server to a client, consider a solution such as the RemoteSocketStream class I've created. It allows you to use a bi-directional path between the client and server to make simple RPC activities possible. The
important thing to remember is that this works good for void method calls. But
for method calls that return values, you might have to recreate multi-threaded call and return handling, or insert synchronization to force isolation through synchronization.
It will depend on whether only one, or multiple threads might use this socket stream for traffic toward the server at the same time. In many cases there are no multi-threading issues, and you can stick synchronized on the method declarations to force synchronization in case the usage changes.
This API has all void methods, so the only synchronization necessary is in the I/O operations toward the server. So that the complete String representing the command goes out in an atomic write operation.
I liked this blog a lot. Keep them comming PLEASE. (This comments are a little of your main topic.) (Please bear in mind that I am just starting with this things.) Also, what is your remote implementation of the input stream, are you taking in consideration position and number of bytes the client whant's (the one that receives the log data). This is a very hyped use case for Jini (separating client interface from actual implementation interfaces), but I would like to see how real people are using it.
Further of your topic: how are u using the UUID; how are you connecting the client with the server? (Yes I did understand some of the article). My problem is the sequence of actions, and who implements these interface. Can you explain the software structure further (possible future blogs :-)?
A quick solution we have been coding involves log4j+log4net with a UDP appender, and multiple clients listening for trace broadcasts. We achieved consistency across .net/j2ee platforms, and created ui consoles and headless services to sink traces. This implimentation seems more polished, good job.
> Also, what is your remote implementation of the input > stream, are you taking in consideration position and > number of bytes the client whant's (the one that receives > the log data).
The client gets an InputStream. This stream does not support mark(). The client is able to use the API as they see fit. In other applications, I've put an ObjectInputStream() on top of the InputStream so that the server can send serialized data to the client.
> Further of your topic: how are u using the UUID; how are > you connecting the client with the server? (Yes I did > understand some of the article). My problem is the > sequence of actions, and who implements these interface. > Can you explain the software structure further (possible > future blogs :-)?
The Uuid is written from the client to the server so that random connections into the server can't occur to exploit that open connection for DOS or other attacks. After the client connects and IDs, the ServerSocket is closed.
Again, you can look at the http://logman.jini.org website for more information and access to the sources.