X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=srcjar_unused%2Forg%2Fapache%2Flog4j%2Fnet%2FSocketHubAppender.java;fp=srcjar_unused%2Forg%2Fapache%2Flog4j%2Fnet%2FSocketHubAppender.java;h=74d7186ea0711eb8ab66b31f2fb3954cf4fc0d7e;hb=65740880573a48adc758bec3939ece9d9ae104dd;hp=0000000000000000000000000000000000000000;hpb=71aa78b8a7d54e5aeb6b278310dfd735efb77477;p=jalview.git diff --git a/srcjar_unused/org/apache/log4j/net/SocketHubAppender.java b/srcjar_unused/org/apache/log4j/net/SocketHubAppender.java new file mode 100644 index 0000000..74d7186 --- /dev/null +++ b/srcjar_unused/org/apache/log4j/net/SocketHubAppender.java @@ -0,0 +1,514 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j.net; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.ObjectOutputStream; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.Vector; + +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.helpers.CyclicBuffer; +import org.apache.log4j.helpers.LogLog; +import org.apache.log4j.spi.LoggingEvent; + +/** + Sends {@link LoggingEvent} objects to a set of remote log servers, + usually a {@link SocketNode SocketNodes}. + +

Acts just like {@link SocketAppender} except that instead of + connecting to a given remote log server, + SocketHubAppender accepts connections from the remote + log servers as clients. It can accept more than one connection. + When a log event is received, the event is sent to the set of + currently connected remote log servers. Implemented this way it does + not require any update to the configuration file to send data to + another remote log server. The remote log server simply connects to + the host and port the SocketHubAppender is running on. + +

The SocketHubAppender does not store events such + that the remote side will events that arrived after the + establishment of its connection. Once connected, events arrive in + order as guaranteed by the TCP protocol. + +

This implementation borrows heavily from the {@link + SocketAppender}. + +

The SocketHubAppender has the following characteristics: + +

+ + @author Mark Womack */ + +public class SocketHubAppender extends AppenderSkeleton { + + /** + The default port number of the ServerSocket will be created on. */ + static final int DEFAULT_PORT = 4560; + + private int port = DEFAULT_PORT; + private Vector oosList = new Vector(); + private ServerMonitor serverMonitor = null; + private boolean locationInfo = false; + private CyclicBuffer buffer = null; + private String application; + private boolean advertiseViaMulticastDNS; + private ZeroConfSupport zeroConf; + + /** + * The MulticastDNS zone advertised by a SocketHubAppender + */ + public static final String ZONE = "_log4j_obj_tcpaccept_appender.local."; + private ServerSocket serverSocket; + + + public SocketHubAppender() { } + + /** + Connects to remote server at address and port. */ + public + SocketHubAppender(int _port) { + port = _port; + startServer(); + } + + /** + Set up the socket server on the specified port. */ + public + void activateOptions() { + if (advertiseViaMulticastDNS) { + zeroConf = new ZeroConfSupport(ZONE, port, getName()); + zeroConf.advertise(); + } + startServer(); + } + + /** + Close this appender. +

This will mark the appender as closed and + call then {@link #cleanUp} method. */ + synchronized + public + void close() { + if(closed) { + return; + } + + LogLog.debug("closing SocketHubAppender " + getName()); + this.closed = true; + if (advertiseViaMulticastDNS) { + zeroConf.unadvertise(); + } + cleanUp(); + + LogLog.debug("SocketHubAppender " + getName() + " closed"); + } + + /** + Release the underlying ServerMonitor thread, and drop the connections + to all connected remote servers. */ + public + void cleanUp() { + // stop the monitor thread + LogLog.debug("stopping ServerSocket"); + serverMonitor.stopMonitor(); + serverMonitor = null; + + // close all of the connections + LogLog.debug("closing client connections"); + while (oosList.size() != 0) { + ObjectOutputStream oos = (ObjectOutputStream)oosList.elementAt(0); + if(oos != null) { + try { + oos.close(); + } catch(InterruptedIOException e) { + Thread.currentThread().interrupt(); + LogLog.error("could not close oos.", e); + } catch(IOException e) { + LogLog.error("could not close oos.", e); + } + + oosList.removeElementAt(0); + } + } + } + + /** + Append an event to all of current connections. */ + public + void append(LoggingEvent event) { + if (event != null) { + // set up location info if requested + if (locationInfo) { + event.getLocationInformation(); + } + if (application != null) { + event.setProperty("application", application); + } + event.getNDC(); + event.getThreadName(); + event.getMDCCopy(); + event.getRenderedMessage(); + event.getThrowableStrRep(); + + if (buffer != null) { + buffer.add(event); + } + } + + // if no event or no open connections, exit now + if ((event == null) || (oosList.size() == 0)) { + return; + } + + // loop through the current set of open connections, appending the event to each + for (int streamCount = 0; streamCount < oosList.size(); streamCount++) { + + ObjectOutputStream oos = null; + try { + oos = (ObjectOutputStream)oosList.elementAt(streamCount); + } + catch (ArrayIndexOutOfBoundsException e) { + // catch this, but just don't assign a value + // this should not really occur as this method is + // the only one that can remove oos's (besides cleanUp). + } + + // list size changed unexpectedly? Just exit the append. + if (oos == null) { + break; + } + + try { + oos.writeObject(event); + oos.flush(); + // Failing to reset the object output stream every now and + // then creates a serious memory leak. + // right now we always reset. TODO - set up frequency counter per oos? + oos.reset(); + } + catch(IOException e) { + if (e instanceof InterruptedIOException) { + Thread.currentThread().interrupt(); + } + // there was an io exception so just drop the connection + oosList.removeElementAt(streamCount); + LogLog.debug("dropped connection"); + + // decrement to keep the counter in place (for loop always increments) + streamCount--; + } + } + } + + /** + The SocketHubAppender does not use a layout. Hence, this method returns + false. */ + public + boolean requiresLayout() { + return false; + } + + /** + The Port option takes a positive integer representing + the port where the server is waiting for connections. */ + public + void setPort(int _port) { + port = _port; + } + + /** + * The App option takes a string value which should be the name of the application getting logged. If property was already set (via system + * property), don't set here. + */ + public + void setApplication(String lapp) { + this.application = lapp; + } + + /** + * Returns value of the Application option. + */ + public + String getApplication() { + return application; + } + + /** + Returns value of the Port option. */ + public + int getPort() { + return port; + } + + /** + * The BufferSize option takes a positive integer representing the number of events this appender will buffer and send to newly connected + * clients. + */ + public + void setBufferSize(int _bufferSize) { + buffer = new CyclicBuffer(_bufferSize); + } + + /** + * Returns value of the bufferSize option. + */ + public + int getBufferSize() { + if (buffer == null) { + return 0; + } else { + return buffer.getMaxSize(); + } + } + + /** + The LocationInfo option takes a boolean value. If true, + the information sent to the remote host will include location + information. By default no location information is sent to the server. */ + public + void setLocationInfo(boolean _locationInfo) { + locationInfo = _locationInfo; + } + + /** + Returns value of the LocationInfo option. */ + public + boolean getLocationInfo() { + return locationInfo; + } + + public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) { + this.advertiseViaMulticastDNS = advertiseViaMulticastDNS; + } + + public boolean isAdvertiseViaMulticastDNS() { + return advertiseViaMulticastDNS; + } + + /** + Start the ServerMonitor thread. */ + private + void startServer() { + serverMonitor = new ServerMonitor(port, oosList); + } + + /** + * Creates a server socket to accept connections. + * @param socketPort port on which the socket should listen, may be zero. + * @return new socket. + * @throws IOException IO error when opening the socket. + */ + protected ServerSocket createServerSocket(final int socketPort) throws IOException { + return new ServerSocket(socketPort); + } + + /** + This class is used internally to monitor a ServerSocket + and register new connections in a vector passed in the + constructor. */ + private class ServerMonitor implements Runnable { + private int port; + private Vector oosList; + private boolean keepRunning; + private Thread monitorThread; + + /** + Create a thread and start the monitor. */ + public + ServerMonitor(int _port, Vector _oosList) { + port = _port; + oosList = _oosList; + keepRunning = true; + monitorThread = new Thread(this); + monitorThread.setDaemon(true); + monitorThread.setName("SocketHubAppender-Monitor-" + port); + monitorThread.start(); + } + + /** + Stops the monitor. This method will not return until + the thread has finished executing. */ + public synchronized void stopMonitor() { + if (keepRunning) { + LogLog.debug("server monitor thread shutting down"); + keepRunning = false; + try { + if (serverSocket != null) { + serverSocket.close(); + serverSocket = null; + } + } catch (IOException ioe) {} + + try { + monitorThread.join(); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + // do nothing? + } + + // release the thread + monitorThread = null; + LogLog.debug("server monitor thread shut down"); + } + } + + private + void sendCachedEvents(ObjectOutputStream stream) throws IOException { + if (buffer != null) { + for (int i = 0; i < buffer.length(); i++) { + stream.writeObject(buffer.get(i)); + } + stream.flush(); + stream.reset(); + } + } + + /** + Method that runs, monitoring the ServerSocket and adding connections as + they connect to the socket. */ + public + void run() { + serverSocket = null; + try { + serverSocket = createServerSocket(port); + serverSocket.setSoTimeout(1000); + } + catch (Exception e) { + if (e instanceof InterruptedIOException || e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + LogLog.error("exception setting timeout, shutting down server socket.", e); + keepRunning = false; + return; + } + + try { + try { + serverSocket.setSoTimeout(1000); + } + catch (SocketException e) { + LogLog.error("exception setting timeout, shutting down server socket.", e); + return; + } + + while (keepRunning) { + Socket socket = null; + try { + socket = serverSocket.accept(); + } + catch (InterruptedIOException e) { + // timeout occurred, so just loop + } + catch (SocketException e) { + LogLog.error("exception accepting socket, shutting down server socket.", e); + keepRunning = false; + } + catch (IOException e) { + LogLog.error("exception accepting socket.", e); + } + + // if there was a socket accepted + if (socket != null) { + try { + InetAddress remoteAddress = socket.getInetAddress(); + LogLog.debug("accepting connection from " + remoteAddress.getHostName() + + " (" + remoteAddress.getHostAddress() + ")"); + + // create an ObjectOutputStream + ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); + if (buffer != null && buffer.length() > 0) { + sendCachedEvents(oos); + } + + // add it to the oosList. OK since Vector is synchronized. + oosList.addElement(oos); + } catch (IOException e) { + if (e instanceof InterruptedIOException) { + Thread.currentThread().interrupt(); + } + LogLog.error("exception creating output stream on socket.", e); + } + } + } + } + finally { + // close the socket + try { + serverSocket.close(); + } catch(InterruptedIOException e) { + Thread.currentThread().interrupt(); + } catch (IOException e) { + // do nothing with it? + } + } + } + } +} +