001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.log4j.varia; 019 020import java.io.DataInputStream; 021import java.io.DataOutputStream; 022import java.io.IOException; 023import java.io.InterruptedIOException; 024import java.net.ServerSocket; 025import java.net.Socket; 026 027import org.apache.log4j.RollingFileAppender; 028import org.apache.log4j.helpers.LogLog; 029 030/** 031 This appender listens on a socket on the port specified by the 032 <b>Port</b> property for a "RollOver" message. When such a message 033 is received, the underlying log file is rolled over and an 034 acknowledgment message is sent back to the process initiating the 035 roll over. 036 037 <p>This method of triggering roll over has the advantage of being 038 operating system independent, fast and reliable. 039 040 <p>A simple application {@link Roller} is provided to initiate the 041 roll over. 042 043 <p>Note that the initiator is not authenticated. Anyone can trigger 044 a rollover. In production environments, it is recommended that you 045 add some form of protection to prevent undesired rollovers. 046 047 048 @author Ceki Gülcü 049 @since version 0.9.0 */ 050public class ExternallyRolledFileAppender extends RollingFileAppender { 051 052 /** 053 The string constant sent to initiate a roll over. Current value of 054 this string constant is <b>RollOver</b>. 055 */ 056 static final public String ROLL_OVER = "RollOver"; 057 058 /** 059 The string constant sent to acknowledge a roll over. Current value of 060 this string constant is <b>OK</b>. 061 */ 062 static final public String OK = "OK"; 063 064 int port = 0; 065 HUP hup; 066 067 /** 068 The default constructor does nothing but calls its super-class 069 constructor. */ 070 public 071 ExternallyRolledFileAppender() { 072 } 073 074 /** 075 The <b>Port</b> [roperty is used for setting the port for 076 listening to external roll over messages. 077 */ 078 public 079 void setPort(int port) { 080 this.port = port; 081 } 082 083 /** 084 Returns value of the <b>Port</b> option. 085 */ 086 public 087 int getPort() { 088 return port; 089 } 090 091 /** 092 Start listening on the port specified by a preceding call to 093 {@link #setPort}. */ 094 public 095 void activateOptions() { 096 super.activateOptions(); 097 if(port != 0) { 098 if(hup != null) { 099 hup.interrupt(); 100 } 101 hup = new HUP(this, port); 102 hup.setDaemon(true); 103 hup.start(); 104 } 105 } 106} 107 108 109class HUP extends Thread { 110 111 int port; 112 ExternallyRolledFileAppender er; 113 114 HUP(ExternallyRolledFileAppender er, int port) { 115 this.er = er; 116 this.port = port; 117 } 118 119 public 120 void run() { 121 while(!isInterrupted()) { 122 try { 123 ServerSocket serverSocket = new ServerSocket(port); 124 while(true) { 125 Socket socket = serverSocket.accept(); 126 LogLog.debug("Connected to client at " + socket.getInetAddress()); 127 new Thread(new HUPNode(socket, er), "ExternallyRolledFileAppender-HUP").start(); 128 } 129 } catch(InterruptedIOException e) { 130 Thread.currentThread().interrupt(); 131 e.printStackTrace(); 132 } catch(IOException e) { 133 e.printStackTrace(); 134 } catch(RuntimeException e) { 135 e.printStackTrace(); 136 } 137 } 138 } 139} 140 141class HUPNode implements Runnable { 142 143 Socket socket; 144 DataInputStream dis; 145 DataOutputStream dos; 146 ExternallyRolledFileAppender er; 147 148 public 149 HUPNode(Socket socket, ExternallyRolledFileAppender er) { 150 this.socket = socket; 151 this.er = er; 152 try { 153 dis = new DataInputStream(socket.getInputStream()); 154 dos = new DataOutputStream(socket.getOutputStream()); 155 } catch(InterruptedIOException e) { 156 Thread.currentThread().interrupt(); 157 e.printStackTrace(); 158 } catch(IOException e) { 159 e.printStackTrace(); 160 } catch(RuntimeException e) { 161 e.printStackTrace(); 162 } 163 } 164 165 public void run() { 166 try { 167 String line = dis.readUTF(); 168 LogLog.debug("Got external roll over signal."); 169 if(ExternallyRolledFileAppender.ROLL_OVER.equals(line)) { 170 synchronized(er) { 171 er.rollOver(); 172 } 173 dos.writeUTF(ExternallyRolledFileAppender.OK); 174 } 175 else { 176 dos.writeUTF("Expecting [RollOver] string."); 177 } 178 dos.close(); 179 } catch(InterruptedIOException e) { 180 Thread.currentThread().interrupt(); 181 LogLog.error("Unexpected exception. Exiting HUPNode.", e); 182 } catch(IOException e) { 183 LogLog.error("Unexpected exception. Exiting HUPNode.", e); 184 } catch(RuntimeException e) { 185 LogLog.error("Unexpected exception. Exiting HUPNode.", e); 186 } 187 } 188} 189