--- /dev/null
+/*
+ * 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.varia;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.apache.log4j.RollingFileAppender;
+import org.apache.log4j.helpers.LogLog;
+
+/**
+ This appender listens on a socket on the port specified by the
+ <b>Port</b> property for a "RollOver" message. When such a message
+ is received, the underlying log file is rolled over and an
+ acknowledgment message is sent back to the process initiating the
+ roll over.
+
+ <p>This method of triggering roll over has the advantage of being
+ operating system independent, fast and reliable.
+
+ <p>A simple application {@link Roller} is provided to initiate the
+ roll over.
+
+ <p>Note that the initiator is not authenticated. Anyone can trigger
+ a rollover. In production environments, it is recommended that you
+ add some form of protection to prevent undesired rollovers.
+
+
+ @author Ceki Gülcü
+ @since version 0.9.0 */
+public class ExternallyRolledFileAppender extends RollingFileAppender {
+
+ /**
+ The string constant sent to initiate a roll over. Current value of
+ this string constant is <b>RollOver</b>.
+ */
+ static final public String ROLL_OVER = "RollOver";
+
+ /**
+ The string constant sent to acknowledge a roll over. Current value of
+ this string constant is <b>OK</b>.
+ */
+ static final public String OK = "OK";
+
+ int port = 0;
+ HUP hup;
+
+ /**
+ The default constructor does nothing but calls its super-class
+ constructor. */
+ public
+ ExternallyRolledFileAppender() {
+ }
+
+ /**
+ The <b>Port</b> [roperty is used for setting the port for
+ listening to external roll over messages.
+ */
+ public
+ void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ Returns value of the <b>Port</b> option.
+ */
+ public
+ int getPort() {
+ return port;
+ }
+
+ /**
+ Start listening on the port specified by a preceding call to
+ {@link #setPort}. */
+ public
+ void activateOptions() {
+ super.activateOptions();
+ if(port != 0) {
+ if(hup != null) {
+ hup.interrupt();
+ }
+ hup = new HUP(this, port);
+ hup.setDaemon(true);
+ hup.start();
+ }
+ }
+}
+
+
+class HUP extends Thread {
+
+ int port;
+ ExternallyRolledFileAppender er;
+
+ HUP(ExternallyRolledFileAppender er, int port) {
+ this.er = er;
+ this.port = port;
+ }
+
+ public
+ void run() {
+ while(!isInterrupted()) {
+ try {
+ ServerSocket serverSocket = new ServerSocket(port);
+ while(true) {
+ Socket socket = serverSocket.accept();
+ LogLog.debug("Connected to client at " + socket.getInetAddress());
+ new Thread(new HUPNode(socket, er), "ExternallyRolledFileAppender-HUP").start();
+ }
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ e.printStackTrace();
+ } catch(IOException e) {
+ e.printStackTrace();
+ } catch(RuntimeException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
+
+class HUPNode implements Runnable {
+
+ Socket socket;
+ DataInputStream dis;
+ DataOutputStream dos;
+ ExternallyRolledFileAppender er;
+
+ public
+ HUPNode(Socket socket, ExternallyRolledFileAppender er) {
+ this.socket = socket;
+ this.er = er;
+ try {
+ dis = new DataInputStream(socket.getInputStream());
+ dos = new DataOutputStream(socket.getOutputStream());
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ e.printStackTrace();
+ } catch(IOException e) {
+ e.printStackTrace();
+ } catch(RuntimeException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void run() {
+ try {
+ String line = dis.readUTF();
+ LogLog.debug("Got external roll over signal.");
+ if(ExternallyRolledFileAppender.ROLL_OVER.equals(line)) {
+ synchronized(er) {
+ er.rollOver();
+ }
+ dos.writeUTF(ExternallyRolledFileAppender.OK);
+ }
+ else {
+ dos.writeUTF("Expecting [RollOver] string.");
+ }
+ dos.close();
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ LogLog.error("Unexpected exception. Exiting HUPNode.", e);
+ } catch(IOException e) {
+ LogLog.error("Unexpected exception. Exiting HUPNode.", e);
+ } catch(RuntimeException e) {
+ LogLog.error("Unexpected exception. Exiting HUPNode.", e);
+ }
+ }
+}
+