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.net;
19  
20  import java.net.Socket;
21  import java.net.ServerSocket;
22  import java.net.InetAddress;
23  import java.io.File;
24  import java.util.Hashtable;
25  
26  import org.apache.log4j.*;
27  import org.apache.log4j.spi.*;
28  
29  
30  /***
31     A {@link SocketNode} based server that uses a different hierarchy
32     for each client.
33  
34     <pre>
35       <b>Usage:</b> java org.apache.log4j.net.SocketServer port configFile configDir
36  
37       where <b>port</b> is a part number where the server listens,
38             <b>configFile</b> is a configuration file fed to the {@link PropertyConfigurator} and
39             <b>configDir</b> is a path to a directory containing configuration files, possibly one for each client host.
40       </pre>
41  
42       <p>The <code>configFile</code> is used to configure the log4j
43       default hierarchy that the <code>SocketServer</code> will use to
44       report on its actions.
45  
46       <p>When a new connection is opened from a previously unknown
47       host, say <code>foo.bar.net</code>, then the
48       <code>SocketServer</code> will search for a configuration file
49       called <code>foo.bar.net.lcf</code> under the directory
50       <code>configDir</code> that was passed as the third argument. If
51       the file can be found, then a new hierarchy is instantiated and
52       configured using the configuration file
53       <code>foo.bar.net.lcf</code>. If and when the host
54       <code>foo.bar.net</code> opens another connection to the server,
55       then the previously configured hierarchy is used.
56  
57       <p>In case there is no file called <code>foo.bar.net.lcf</code>
58       under the directory <code>configDir</code>, then the
59       <em>generic</em> hierarchy is used. The generic hierarchy is
60       configured using a configuration file called
61       <code>generic.lcf</code> under the <code>configDir</code>
62       directory. If no such file exists, then the generic hierarchy will be
63       identical to the log4j default hierarchy.
64  
65       <p>Having different client hosts log using different hierarchies
66       ensures the total independence of the clients with respect to
67       their logging settings.
68  
69       <p>Currently, the hierarchy that will be used for a given request
70       depends on the IP address of the client host. For example, two
71       separate applicatons running on the same host and logging to the
72       same server will share the same hierarchy. This is perfectly safe
73       except that it might not provide the right amount of independence
74       between applications. The <code>SocketServer</code> is intended
75       as an example to be enhanced in order to implement more elaborate
76       policies.
77  
78  
79      @author  Ceki G&uuml;lc&uuml;
80  
81      @since 1.0 */
82  
83  public class SocketServer  {
84  
85    static String GENERIC = "generic";
86    static String CONFIG_FILE_EXT = ".lcf";
87  
88    static Logger cat = Logger.getLogger(SocketServer.class);
89    static SocketServer server;
90    static int port;
91  
92    // key=inetAddress, value=hierarchy
93    Hashtable hierarchyMap;
94    LoggerRepository genericHierarchy;
95    File dir;
96  
97    public
98    static
99    void main(String argv[]) {
100     if(argv.length == 3)
101       init(argv[0], argv[1], argv[2]);
102     else
103       usage("Wrong number of arguments.");
104 
105     try {
106       cat.info("Listening on port " + port);
107       ServerSocket serverSocket = new ServerSocket(port);
108       while(true) {
109 	cat.info("Waiting to accept a new client.");
110 	Socket socket = serverSocket.accept();
111 	InetAddress inetAddress =  socket.getInetAddress();
112 	cat.info("Connected to client at " + inetAddress);
113 
114 	LoggerRepository h = (LoggerRepository) server.hierarchyMap.get(inetAddress);
115 	if(h == null) {
116 	  h = server.configureHierarchy(inetAddress);
117 	}
118 
119 	cat.info("Starting new socket node.");
120 	new Thread(new SocketNode(socket, h)).start();
121       }
122     }
123     catch(Exception e) {
124       e.printStackTrace();
125     }
126   }
127 
128 
129   static
130   void  usage(String msg) {
131     System.err.println(msg);
132     System.err.println(
133       "Usage: java " +SocketServer.class.getName() + " port configFile directory");
134     System.exit(1);
135   }
136 
137   static
138   void init(String portStr, String configFile, String dirStr) {
139     try {
140       port = Integer.parseInt(portStr);
141     }
142     catch(java.lang.NumberFormatException e) {
143       e.printStackTrace();
144       usage("Could not interpret port number ["+ portStr +"].");
145     }
146 
147     PropertyConfigurator.configure(configFile);
148 
149     File dir = new File(dirStr);
150     if(!dir.isDirectory()) {
151       usage("["+dirStr+"] is not a directory.");
152     }
153     server = new SocketServer(dir);
154   }
155 
156 
157   public
158   SocketServer(File directory) {
159     this.dir = directory;
160     hierarchyMap = new Hashtable(11);
161   }
162 
163   // This method assumes that there is no hiearchy for inetAddress
164   // yet. It will configure one and return it.
165   LoggerRepository configureHierarchy(InetAddress inetAddress) {
166     cat.info("Locating configuration file for "+inetAddress);
167     // We assume that the toSting method of InetAddress returns is in
168     // the format hostname/d1.d2.d3.d4 e.g. torino/192.168.1.1
169     String s = inetAddress.toString();
170     int i = s.indexOf("/");
171     if(i == -1) {
172       cat.warn("Could not parse the inetAddress ["+inetAddress+
173 	       "]. Using default hierarchy.");
174       return genericHierarchy();
175     } else {
176       String key = s.substring(0, i);
177 
178       File configFile = new File(dir, key+CONFIG_FILE_EXT);
179       if(configFile.exists()) {
180 	Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
181 	hierarchyMap.put(inetAddress, h);
182 
183 	new PropertyConfigurator().doConfigure(configFile.getAbsolutePath(), h);
184 
185 	return h;
186       } else {
187 	cat.warn("Could not find config file ["+configFile+"].");
188 	return genericHierarchy();
189       }
190     }
191   }
192 
193   LoggerRepository  genericHierarchy() {
194     if(genericHierarchy == null) {
195       File f = new File(dir, GENERIC+CONFIG_FILE_EXT);
196       if(f.exists()) {
197 	genericHierarchy = new Hierarchy(new RootLogger(Level.DEBUG));
198 	new PropertyConfigurator().doConfigure(f.getAbsolutePath(), genericHierarchy);
199       } else {
200 	cat.warn("Could not find config file ["+f+
201 		 "]. Will use the default hierarchy.");
202 	genericHierarchy = LogManager.getLoggerRepository();
203       }
204     }
205     return genericHierarchy;
206   }
207 }