View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j.varia;
19  
20  import java.io.DataInputStream;
21  import java.io.DataOutputStream;
22  import java.io.IOException;
23  import java.io.InterruptedIOException;
24  import java.net.ServerSocket;
25  import java.net.Socket;
26  
27  import org.apache.log4j.RollingFileAppender;
28  import org.apache.log4j.helpers.LogLog;
29  
30  /**
31     This appender listens on a socket on the port specified by the
32     <b>Port</b> property for a "RollOver" message. When such a message
33     is received, the underlying log file is rolled over and an
34     acknowledgment message is sent back to the process initiating the
35     roll over.
36  
37     <p>This method of triggering roll over has the advantage of being
38     operating system independent, fast and reliable.
39  
40     <p>A simple application {@link Roller} is provided to initiate the
41     roll over.
42  
43     <p>Note that the initiator is not authenticated. Anyone can trigger
44     a rollover. In production environments, it is recommended that you
45     add some form of protection to prevent undesired rollovers.
46  
47  
48     @author Ceki G&uuml;lc&uuml;
49     @since version 0.9.0 */
50  public class ExternallyRolledFileAppender extends RollingFileAppender {
51  
52    /**
53       The string constant sent to initiate a roll over.   Current value of
54       this string constant is <b>RollOver</b>.
55    */
56    static final public String ROLL_OVER = "RollOver";
57  
58    /**
59       The string constant sent to acknowledge a roll over.   Current value of
60        this string constant is <b>OK</b>.
61    */
62    static final public String OK = "OK";
63  
64    int port = 0;
65    HUP hup;
66  
67    /**
68       The default constructor does nothing but calls its super-class
69       constructor.  */
70    public
71    ExternallyRolledFileAppender() {
72    }
73  
74    /**
75       The <b>Port</b> [roperty is used for setting the port for
76       listening to external roll over messages.
77    */
78    public
79    void setPort(int port) {
80      this.port = port;
81    }
82  
83    /**
84       Returns value of the <b>Port</b> option.
85     */
86    public
87    int getPort() {
88      return port;
89    }
90  
91    /**
92       Start listening on the port specified by a preceding call to
93       {@link #setPort}.  */
94    public
95    void activateOptions() {
96      super.activateOptions();
97      if(port != 0) {
98        if(hup != null) {
99  	hup.interrupt();
100       }
101       hup = new HUP(this, port);
102       hup.setDaemon(true);
103       hup.start();
104     }
105   }
106 }
107 
108 
109 class 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 
141 class 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