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