
Author: seb Date: Tue Dec 5 16:31:33 2006 New Revision: 1063 Added: logback/trunk/logback-examples/src/main/java/chapter7/ logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncher.java logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherClient.java logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherServer.java logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java logback/trunk/logback-examples/src/main/java/chapter7/mdc1.xml logback/trunk/logback-site/src/site/xdocTemplates/manual/mdc.xml Modified: logback/trunk/logback-site/src/site/xdocTemplates/manual/filters.xml Log: On going work on chapter 7 Added: logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncher.java ============================================================================== --- (empty file) +++ logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncher.java Tue Dec 5 16:31:33 2006 @@ -0,0 +1,26 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package chapter7; + +import java.rmi.Remote; +import java.rmi.RemoteException; + + +/** + * NumberCruncher factors positive integers. + */ +public interface NumberCruncher extends Remote { + /** + * Factor a positive integer <code>number</code> and return its + * <em>distinct</em> factor's as an integer array. + * */ + int[] factor(int number) throws RemoteException; +} Added: logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherClient.java ============================================================================== --- (empty file) +++ logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherClient.java Tue Dec 5 16:31:33 2006 @@ -0,0 +1,83 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package chapter7; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.rmi.Naming; +import java.rmi.RemoteException; + + +/** + * NumberCruncherClient is a simple client for factoring integers. A + * remote NumberCruncher is contacted and asked to factor an + * integer. The factors returned by the {@link NumberCruncherServer} + * are displayed on the screen. + * */ +public class NumberCruncherClient { + public static void main(String[] args) { + if (args.length == 1) { + try { + String url = "rmi://" + args[0] + "/Factor"; + NumberCruncher nc = (NumberCruncher) Naming.lookup(url); + loop(nc); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + usage("Wrong number of arguments."); + } + } + + static void usage(String msg) { + System.err.println(msg); + System.err.println("Usage: java chapter7.NumberCruncherClient HOST\n" + + " where HOST is the machine where the NumberCruncherServer is running."); + System.exit(1); + } + + static void loop(NumberCruncher nc) { + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + int i = 0; + + while (true) { + System.out.print("Enter a number to factor, '-1' to quit: "); + + try { + i = Integer.parseInt(in.readLine()); + } catch (Exception e) { + e.printStackTrace(); + } + + if (i == -1) { + System.out.print("Exiting loop."); + + return; + } else { + try { + System.out.println("Will attempt to factor " + i); + + int[] factors = nc.factor(i); + System.out.print("The factors of " + i + " are"); + + for (int k = 0; k < factors.length; k++) { + System.out.print(" " + factors[k]); + } + + System.out.println("."); + } catch (RemoteException e) { + System.err.println("Could not factor " + i); + e.printStackTrace(); + } + } + } + } +} Added: logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherServer.java ============================================================================== --- (empty file) +++ logback/trunk/logback-examples/src/main/java/chapter7/NumberCruncherServer.java Tue Dec 5 16:31:33 2006 @@ -0,0 +1,159 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package chapter7; + +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.UnicastRemoteObject; +import java.util.Vector; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.MDC; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; + + +/** + * A simple NumberCruncher implementation that logs its progress when + * factoring numbers. The purpose of the whole exercise is to show the + * use of mapped diagnostic contexts in order to distinguish the log + * output from different client requests. + * */ +public class NumberCruncherServer extends UnicastRemoteObject + implements NumberCruncher { + + private static final long serialVersionUID = 1L; + + static Logger logger = LoggerFactory.getLogger(NumberCruncherServer.class); + + public NumberCruncherServer() throws RemoteException { + } + + public int[] factor(int number) throws RemoteException { + // The client's host is an important source of information. + try { + MDC.put("client", NumberCruncherServer.getClientHost()); + } catch (java.rmi.server.ServerNotActiveException e) { + logger.warn("Caught unexpected ServerNotActiveException.", e); + } + + // The information contained within the request is another source + // of distinctive information. It might reveal the users name, + // date of request, request ID etc. In servlet type environments, + // useful information is contained in the HttpRequest or in the + // HttpSession. + MDC.put("number", String.valueOf(number)); + + logger.info("Beginning to factor."); + + if (number <= 0) { + throw new IllegalArgumentException(number + + " is not a positive integer."); + } else if (number == 1) { + return new int[] { 1 }; + } + + Vector<Integer> factors = new Vector<Integer>(); + int n = number; + + for (int i = 2; (i <= n) && ((i * i) <= number); i++) { + // It is bad practice to place log requests within tight loops. + // It is done here to show interleaved log output from + // different requests. + logger.debug("Trying " + i + " as a factor."); + + if ((n % i) == 0) { + logger.info("Found factor " + i); + factors.addElement(new Integer(i)); + + do { + n /= i; + } while ((n % i) == 0); + } + + // Placing artificial delays in tight-loops will also lead to + // sub-optimal resuts. :-) + delay(100); + } + + if (n != 1) { + logger.info("Found factor " + n); + factors.addElement(new Integer(n)); + } + + int len = factors.size(); + + int[] result = new int[len]; + + for (int i = 0; i < len; i++) { + result[i] = ((Integer) factors.elementAt(i)).intValue(); + } + + // clean up + MDC.remove("client"); + MDC.remove("number"); + + return result; + } + + static void usage(String msg) { + System.err.println(msg); + System.err.println("Usage: java chapter7.NumberCruncherServer configFile\n" + + " where configFile is a logback configuration file."); + System.exit(1); + } + + public static void delay(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + } + } + + public static void main(String[] args) { + if (args.length != 1) { + usage("Wrong number of arguments."); + } + + String configFile = args[0]; + + if (configFile.endsWith(".xml")) { + try { + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(lc); + lc.shutdownAndReset(); + configurator.doConfigure(args[0]); + } catch (JoranException je) { + je.printStackTrace(); + } + } + + NumberCruncherServer ncs; + + try { + ncs = new NumberCruncherServer(); + logger.info("Creating registry."); + + Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT); + registry.rebind("Factor", ncs); + logger.info("NumberCruncherServer bound and ready."); + } catch (Exception e) { + logger.error("Could not bind NumberCruncherServer.", e); + + return; + } + } +} Added: logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java ============================================================================== --- (empty file) +++ logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java Tue Dec 5 16:31:33 2006 @@ -0,0 +1,52 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package chapter7; + +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.MDC; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.core.ConsoleAppender; + +public class SimpleMDC { + static public void main(String[] args) throws Exception { + // You can put values in the MDC at any time. We first put the + // first name + MDC.put("first", "Dorothy"); + + // Configure logback + PatternLayout layout = new PatternLayout(); + layout.setPattern("%X{first} %X{last} - %m%n"); + layout.start(); + ConsoleAppender appender = new ConsoleAppender(); + appender.setLayout(layout); + appender.start(); + Logger root = (Logger)LoggerFactory.getLogger("root"); + root.addAppender(appender); + + // get a logger + Logger logger = (Logger)LoggerFactory.getLogger(SimpleMDC.class); + + // We now put the last name + MDC.put("last", "Parker"); + + // The most beautiful two words in the English language according + // to Dorothy Parker: + logger.info("Check enclosed."); + logger.debug("The most beautiful two words in English."); + + MDC.put("first", "Richard"); + MDC.put("last", "Nixon"); + logger.info("I am not a crook."); + logger.info("Attributed to the former US president. 17 Nov 1973."); + } +} Added: logback/trunk/logback-examples/src/main/java/chapter7/mdc1.xml ============================================================================== --- (empty file) +++ logback/trunk/logback-examples/src/main/java/chapter7/mdc1.xml Tue Dec 5 16:31:33 2006 @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<configuration> + + <appender name="CONSOLE" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%-4r [%thread] %-5level C:%X{client} N:%X{number} - %msg%n</Pattern> + </layout> + </appender> + + <root> + <level value ="debug"/> + <appender-ref ref="CONSOLE"/> + </root> +</configuration> Modified: logback/trunk/logback-site/src/site/xdocTemplates/manual/filters.xml ============================================================================== --- logback/trunk/logback-site/src/site/xdocTemplates/manual/filters.xml (original) +++ logback/trunk/logback-site/src/site/xdocTemplates/manual/filters.xml Tue Dec 5 16:31:33 2006 @@ -157,11 +157,11 @@ </p> <table> - <th> - <td>Name</td> - <td>Type</td> - <td>Description</td> - </th> + <tr> + <th>Name</th> + <th>Type</th> + <th>Description</th> + </tr> <tr> <td>event </td> @@ -285,21 +285,21 @@ <em>Example 6.1: Basic event evaluator usage (logback-examples/src/main/java/chapter6/turboFilters.xml)</em> <div class="source"><pre><configuration> - <turboFilter class="ch.qos.logback.classic.turbo.MDCFilter"> - <MDCKey>username</MDCKey> - <Value>sebastien</Value> - <OnMatch>ACCEPT</OnMatch> - </turboFilter> + <turboFilter class="ch.qos.logback.classic.turbo.MDCFilter"> + <MDCKey>username</MDCKey> + <Value>sebastien</Value> + <OnMatch>ACCEPT</OnMatch> + </turboFilter> - <turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter"> - <Marker>billing</Marker> - <OnMatch>DENY</OnMatch> - </turboFilter> + <turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter"> + <Marker>billing</Marker> + <OnMatch>DENY</OnMatch> + </turboFilter> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%date [%thread] %-5level %logger - %msg%n</Pattern> - </layout> + </layout> </appender> <root> @@ -358,7 +358,7 @@ </p> - <h3>Logback Access</h3> + <h2>Logback Access</h2> <p> Logback access benefits from most of the possibilities available @@ -368,7 +368,7 @@ <code>TurboFilter</code> objects are not available to the access module. </p> - <h2>Filters</h2> + <h3>Filters</h3> <p> <code>EvaluatorFilter</code> objects, with their expressions, are available to Added: logback/trunk/logback-site/src/site/xdocTemplates/manual/mdc.xml ============================================================================== --- (empty file) +++ logback/trunk/logback-site/src/site/xdocTemplates/manual/mdc.xml Tue Dec 5 16:31:33 2006 @@ -0,0 +1,478 @@ +<document> +<!-- + + Warning: do not use any auto-format function on this file. + Since "source" divs use pre as white-space, it affects the + look of the code parts in this document. + + --> + + <body> + <h2>Chapter 7: Mapped Diagnostic Context</h2> + <div class="author"> + Authors: Ceki Gülcü, Sébastien Pennec + </div> + + <table> + <tr> + <td valign="top" align="top"> + <a rel="license" + href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + <img alt="Creative Commons License" + style="border-width: 0" + src="http://creativecommons.org/images/public/somerights20.png" /> + </a> + </td> + <td> + <p>Copyright © 2000-2006, QOS.ch</p> + + <p> + <!--Creative Commons License--> + This work is licensed under a + <a rel="license" + href="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + Creative Commons + Attribution-NonCommercial-ShareAlike 2.5 + License + </a>. + <!--/Creative Commons License--> + </p> + </td> + </tr> + </table> + + <p> + One of the design goals of logback is to audit and debug complex distributed applications. + Most real-world distributed systems need to deal with multiple clients simultaneously. + In a typical multithreaded implementation of such a system, different threads will handle + different clients. A possible but discouraged approach to differentiate the logging output of + one client from another consists of instantiating a new and separate logger for each client. + This technique promotes the proliferation of loggers and considerably increases + their management overhead. + </p> + <p> + A lighter technique consists of uniquely stamping each + log request servicing a given client. Neil Harrison described this method in the book + <em>"Patterns for Logging Diagnostic Messages,"</em> in + Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, + and F. Buschmann (Addison-Wesley, 1997). Logback offers a variant of this technique: + Mapped Diagnostic Contexts (MDC). + </p> + + <p> + To uniquely stamp each request, the user puts contextual information into the + <code><a href="../xref/ch/qos/logback/classic/MDC.html">MDC</a></code>, + the abbreviation of Mapped Diagnostic Context. + The public interface of the MDC class is shown below. + </p> + +<div class="source"><pre>package ch.qos.logback.classic; + +public class MDC { + //Put a context value as identified by <em>key</em> + //into the current thread's context map. + <b>public static void put(String key, String val);</b> + + //Get the context identified by the <code>key</code> parameter. + <b>public static String get(String key);</b> + + //Remove the the context identified by the <code>key</code> parameter. + <b>public static void remove(String key);</b> + + //Clear all entries in the MDC. + <b>public static void clear();</b> + + //Returns the keys in the MDC as a Set. The returned value can be null. + <b>public static Set<String> getKeys();</b> +}</pre></div> + + <p> + The <code>MDC</code> class contains only static methods. + It lets the developer place information in a “diagnostic context” that can be + subsequently retrieved by certain logback components. The + <code>MDC</code> manages contextual information on a per thread basis. + Typically, while starting to service a new client request, the developer will + insert pertinent contextual information, such as the client id, client's IP + address, request parameters etc. into the <code>MDC</code>. Logback components, + if appropriately configured, will automatically include this information + in each log entry. + </p> + + <p> + The next application named + <code><a href="../xref/chapter7/SimpleMDC.html">SimpleMDC</a></code> + demonstrates this basic principle. + </p> +<em>Example 7.1: Basic MDC usage (<a href="../xref/chapter7/SimpleMDC.html"> +logback-examples/src/main/java/chapter7/SimpleMDC.java)</a></em> +<div class="source"><pre>package chapter7; + +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.MDC; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.core.ConsoleAppender; + +public class SimpleMDC { + static public void main(String[] args) throws Exception { + // You can put values in the MDC at any time. We first put the + // first name + <b>MDC.put("first", "Dorothy");</b> + + // Configure logback + PatternLayout layout = new PatternLayout(); + layout.setPattern("%X{first} %X{last} - %m%n"); + layout.start(); + ConsoleAppender appender = new ConsoleAppender(); + appender.setLayout(layout); + appender.start(); + Logger root = (Logger)LoggerFactory.getLogger("root"); + root.addAppender(appender); + + // get a logger + Logger logger = (Logger)LoggerFactory.getLogger(SimpleMDC.class); + + // We now put the last name + <b>MDC.put("last", "Parker");</b> + + // The most beautiful two words in the English language according + // to Dorothy Parker: + logger.info("Check enclosed."); + logger.debug("The most beautiful two words in English."); + + MDC.put("first", "Richard"); + MDC.put("last", "Nixon"); + logger.info("I am not a crook."); + logger.info("Attributed to the former US president. 17 Nov 1973."); + } +}</pre></div> + + <p> + The main method starts by associating the value <em>Dorothy</em> with + the key <em>first</em> in the <code>MDC</code>. You can place as many + value/key associations in the <code>MDC</code> as you wish. + Multiple insertions with the same key will overwrite older values. + The code then proceeds to configure logback. + Note the usage of the <em>%X</em> specifier within the + <code>PatternLayout</code> conversion pattern. The <em>%X</em> + conversion specifier is employed twice, once for the key <em>first</em> + and once for the key <em>last</em>. After configuring the root logger, + the code associates the value <em>Parker</em> with the key <em>last</em>. + It then invokes the logger twice with different messages. + The code finishes by setting the <code>MDC</code> to different values + and issuing several logging requests. Running SimpleMDC yields: + </p> + +<div class="source"><pre>Dorothy Parker - Check enclosed. +Dorothy Parker - The most beautiful two words in English. +Richard Nixon - I am not a crook. +Richard Nixon - Attributed to the former US president. 17 Nov 1973.</pre></div> + + + <p> + The <code>SimpleMDC</code> application illustrates how logback layouts, + if configured appropriately, automatically output <code>MDC</code> information. + Moreover, the information placed into the <code>MDC</code> can be used by + multiple logger invocations. + </p> + + <p> + Mapped Diagnostic Contexts shine brightest within client server architectures. + Typically, multiple clients will be served by multiple threads on the server. + Although the methods in the <code>MDC</code> class are static, + the diagnostic context is managed on a per thread basis, allowing each server + thread to bear a distinct <code>MDC</code> stamp. <code>MDC</code> operations + such as <code>put()</code> and <code>get()</code> affect the <code>MDC</code> + of the <em>current</em> thread only. The <code>MDC</code> in other threads remain + unaffected. Given that <code>MDC</code> information is managed on a + per thread basis, each thread will have its own copy of the <code>MDC</code>. + Thus, there is no need for the developer to worry about thread-safety or + synchronization when programming with the <code>MDC</code> because + it safely and transparently handles these issues. + </p> + + <p> + The next example is somewhat more advanced. + It shows how the <code>MDC</code> can be used in a client-server setting. + The server-side implements the <code>NumberCruncher</code> interface shown in + Example 7.2 below. <code>The NumberCruncher</code> interface contains a single + method named <code>factor()</code>. Using RMI technology, client invokes the + <code>factor()</code> method of the server application to retrieve the distinct + factors of an integer. + </p> + +<em>Example 7.2: The service interface (<a href="../xref/chapter7/NumberCruncher.html"> +logback-examples/src/main/java/chapter7/NumberCruncher.java)</a></em> +<div class="source"><pre>package chapter7; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * NumberCruncher factors positive integers. + */ +public interface NumberCruncher extends Remote { + /** + * Factor a positive integer <code>number</code> and return its + * <em>distinct</em> factor's as an integer array. + * */ + int[] factor(int number) throws RemoteException; +}</pre></div> + + <p> + The <code>NumberCruncherServer</code> application, listed in Example 7.3 below, + implements the <code>NumberCruncher</code> interface. Its main method exports + an RMI Registry on the local host that accepts requests on a well-known port. + </p> + +<em>Example 7.2: The server side (<a href="../xref/chapter7/NumberCruncherServer.html"> +logback-examples/src/main/java/chapter7/NumberCruncherServer.java)</a></em> +<div class="source"><pre>package chapter7; + +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.UnicastRemoteObject; +import java.util.Vector; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.MDC; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; + + +/** + * A simple NumberCruncher implementation that logs its progress when + * factoring numbers. The purpose of the whole exercise is to show the + * use of mapped diagnostic contexts in order to distinguish the log + * output from different client requests. + * */ +public class NumberCruncherServer extends UnicastRemoteObject + implements NumberCruncher { + + private static final long serialVersionUID = 1L; + + static Logger logger = LoggerFactory.getLogger(NumberCruncherServer.class); + + public NumberCruncherServer() throws RemoteException { + } + + public int[] factor(int number) throws RemoteException { + // The client's host is an important source of information. + try { + <b>MDC.put("client", NumberCruncherServer.getClientHost());</b> + } catch (java.rmi.server.ServerNotActiveException e) { + logger.warn("Caught unexpected ServerNotActiveException.", e); + } + + // The information contained within the request is another source + // of distinctive information. It might reveal the users name, + // date of request, request ID etc. In servlet type environments, + // useful information is contained in the HttpRequest or in the + // HttpSession. + <b>MDC.put("number", String.valueOf(number));</b> + + logger.info("Beginning to factor."); + + if (number <= 0) { + throw new IllegalArgumentException(number + + " is not a positive integer."); + } else if (number == 1) { + return new int[] { 1 }; + } + + Vector<Integer> factors = new Vector<Integer>(); + int n = number; + + for (int i = 2; (i <= n) && ((i * i) <= number); i++) { + // It is bad practice to place log requests within tight loops. + // It is done here to show interleaved log output from + // different requests. + logger.debug("Trying " + i + " as a factor."); + + if ((n % i) == 0) { + logger.info("Found factor " + i); + factors.addElement(new Integer(i)); + + do { + n /= i; + } while ((n % i) == 0); + } + + // Placing artificial delays in tight-loops will also lead to + // sub-optimal resuts. :-) + delay(100); + } + + if (n != 1) { + logger.info("Found factor " + n); + factors.addElement(new Integer(n)); + } + + int len = factors.size(); + + int[] result = new int[len]; + + for (int i = 0; i < len; i++) { + result[i] = ((Integer) factors.elementAt(i)).intValue(); + } + + <b>// clean up + MDC.remove("client"); + MDC.remove("number");</b> + + return result; + } + + static void usage(String msg) { + System.err.println(msg); + System.err.println("Usage: java chapter7.NumberCruncherServer configFile\n" + + " where configFile is a logback configuration file."); + System.exit(1); + } + + public static void delay(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + } + } + + public static void main(String[] args) { + if (args.length != 1) { + usage("Wrong number of arguments."); + } + + String configFile = args[0]; + + if (configFile.endsWith(".xml")) { + try { + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(lc); + lc.shutdownAndReset(); + configurator.doConfigure(args[0]); + } catch (JoranException je) { + je.printStackTrace(); + } + } + + NumberCruncherServer ncs; + + try { + ncs = new NumberCruncherServer(); + logger.info("Creating registry."); + + Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT); + registry.rebind("Factor", ncs); + logger.info("NumberCruncherServer bound and ready."); + } catch (Exception e) { + logger.error("Could not bind NumberCruncherServer.", e); + + return; + } + } +}</pre></div> + + <p> + The implementation of the <code>factor(int number)</code> method is + of particular relevance. It starts by putting the client's hostname into the + <code>MDC</code> under the key <em>client</em>. The number to factor, + as requested by the client, is put into the <code>MDC</code> under the key + <em>number</em>. After computing the distinct factors of the integer + parameter, the result is returned to the client. Before returning the + result however, the values for the <em>client</em> and <em>number</em> are + cleared by calling the <code>MDC.remove(9</code> method. Normally, + a <code>put()</code> operation should be balanced by the corresponding + <code>remove()</code> operation. Otherwise, the <code>MDC</code> will + contain stale values for certain keys. We would recommend that whenever + possible <code>remove()</code> operations be performed within finally blocks, + ensuring their invocation regardless of the execution path of the code. + </p> + + <p> + After these theoretical explanations, we are ready to run the number + cruncher example. Start the server with the following command: + </p> + +<div class="source"><pre>java chapter7.NumberCruncherServer src/main/java/chapter7/mdc1.xml</pre></div> + + <p> + The <em>mdc1.xml</em> configuration file is listed below: + </p> + +<div class="source"><pre><configuration> + + <appender name="CONSOLE" + class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%-4r [%thread] %-5level <b>C:%X{client} N:%X{number}</b> - %msg%n</Pattern> + </layout> + </appender> + + <root> + <level value ="debug"/> + <appender-ref ref="CONSOLE"/> + </root> +</configuration></pre></div> + + <p> + Note the use of the <em>%X</em> conversion specifier within the + <span class="option">Pattern</span> option. + </p> + + <p> + The following command starts an instance of <code>NumberCruncherClient</code> + application: + </p> + +<div class="source"><pre>java chapter7.NumberCruncherClient <em>hostname</em></pre></div> + + <p> + where <em>hostname</em> is the host where the + <code>NumberCruncherServer</code> is running + </p> + + <p> + Executing multiple instances of the client and requesting the server to factor + the numbers 129 from the first client and shortly thereafter + the number 71 from the second client, the server outputs the following (edited to fit): + </p> + +<div class="source"><pre> +70984 [RMI TCP Connection(4)-192.168.1.6] INFO C:192.168.1.6 N:129 - Beginning to factor. +70984 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 2 as a factor. +71093 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 3 as a factor. +71093 [RMI TCP Connection(4)-192.168.1.6] INFO C:192.168.1.6 N:129 - Found factor 3 +71187 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 4 as a factor. +71297 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 5 as a factor. +71390 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 6 as a factor. +71453 [RMI TCP Connection(5)-192.168.1.6] INFO C:192.168.1.6 N:71 - Beginning to factor. +71453 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 2 as a factor. +71484 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 7 as a factor. +71547 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 3 as a factor. +71593 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 8 as a factor. +71656 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 4 as a factor. +71687 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 9 as a factor. +71750 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 5 as a factor. +71797 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 10 as a factor. +71859 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 6 as a factor. +71890 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:192.168.1.6 N:129 - Trying 11 as a factor. +71953 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 7 as a factor. +72000 [RMI TCP Connection(4)-192.168.1.6] INFO C:192.168.1.6 N:129 - Found factor 43 +72062 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:192.168.1.6 N:71 - Trying 8 as a factor. +72156 [RMI TCP Connection(5)-192.168.1.6] INFO C:192.168.1.6 N:71 - Found factor 71</pre></div> + + + + + + + + + </body> +</document> \ No newline at end of file