001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package chapters.mdc;
015
016import java.rmi.RemoteException;
017import java.rmi.registry.LocateRegistry;
018import java.rmi.registry.Registry;
019import java.rmi.server.UnicastRemoteObject;
020import java.util.Vector;
021
022import org.slf4j.Logger;
023import org.slf4j.LoggerFactory;
024import org.slf4j.MDC;
025
026import ch.qos.logback.classic.LoggerContext;
027import ch.qos.logback.classic.joran.JoranConfigurator;
028import ch.qos.logback.core.joran.spi.JoranException;
029
030/**
031 * A simple NumberCruncher implementation that logs its progress when
032 * factoring numbers. The purpose of the whole exercise is to show the
033 * use of mapped diagnostic contexts in order to distinguish the log
034 * output from different client requests.
035 * */
036public class NumberCruncherServer extends UnicastRemoteObject implements NumberCruncher {
037
038    private static final long serialVersionUID = 1L;
039
040    static Logger logger = LoggerFactory.getLogger(NumberCruncherServer.class);
041
042    public NumberCruncherServer() throws RemoteException {
043    }
044
045    public int[] factor(int number) throws RemoteException {
046        // The client's host is an important source of information.
047        try {
048            MDC.put("client", NumberCruncherServer.getClientHost());
049        } catch (java.rmi.server.ServerNotActiveException e) {
050            logger.warn("Caught unexpected ServerNotActiveException.", e);
051        }
052
053        // The information contained within the request is another source
054        // of distinctive information. It might reveal the users name,
055        // date of request, request ID etc. In servlet type environments,
056        // useful information is contained in the HttpRequest or in the
057        // HttpSession.
058        MDC.put("number", String.valueOf(number));
059
060        logger.info("Beginning to factor.");
061
062        if (number <= 0) {
063            throw new IllegalArgumentException(number + " is not a positive integer.");
064        } else if (number == 1) {
065            return new int[] { 1 };
066        }
067
068        Vector<Integer> factors = new Vector<Integer>();
069        int n = number;
070
071        for (int i = 2; (i <= n) && ((i * i) <= number); i++) {
072            // It is bad practice to place log requests within tight loops.
073            // It is done here to show interleaved log output from
074            // different requests.
075            logger.debug("Trying " + i + " as a factor.");
076
077            if ((n % i) == 0) {
078                logger.info("Found factor " + i);
079                factors.addElement(i);
080
081                do {
082                    n /= i;
083                } while ((n % i) == 0);
084            }
085
086            // Placing artificial delays in tight loops will also lead to
087            // sub-optimal resuts. :-)
088            delay(100);
089        }
090
091        if (n != 1) {
092            logger.info("Found factor " + n);
093            factors.addElement(n);
094        }
095
096        int len = factors.size();
097
098        int[] result = new int[len];
099
100        for (int i = 0; i < len; i++) {
101            result[i] = ((Integer) factors.elementAt(i)).intValue();
102        }
103
104        // clean up
105        MDC.remove("client");
106        MDC.remove("number");
107
108        return result;
109    }
110
111    static void usage(String msg) {
112        System.err.println(msg);
113        System.err.println("Usage: java chapters.mdc.NumberCruncherServer configFile\n" + "   where configFile is a logback configuration file.");
114        System.exit(1);
115    }
116
117    public static void delay(int millis) {
118        try {
119            Thread.sleep(millis);
120        } catch (InterruptedException e) {
121        }
122    }
123
124    public static void main(String[] args) {
125        if (args.length != 1) {
126            usage("Wrong number of arguments.");
127        }
128
129        String configFile = args[0];
130
131        if (configFile.endsWith(".xml")) {
132            try {
133                LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
134                JoranConfigurator configurator = new JoranConfigurator();
135                configurator.setContext(lc);
136                lc.reset();
137                configurator.doConfigure(args[0]);
138            } catch (JoranException je) {
139                je.printStackTrace();
140            }
141        }
142
143        NumberCruncherServer ncs;
144
145        try {
146            ncs = new NumberCruncherServer();
147            logger.info("Creating registry.");
148
149            Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
150            registry.rebind("Factor", ncs);
151            logger.info("NumberCruncherServer bound and ready.");
152        } catch (Exception e) {
153            logger.error("Could not bind NumberCruncherServer.", e);
154
155            return;
156        }
157    }
158}