1:/*
  2: * This is a modified version of an extract from Java Examples in a
  3: * Nutshell.  Modifications have been made in order to enhance a code
  4: * inspection exercise. -Mark Ardis, RHIT, 1/24/2003
  5: *
  6: * Copyright (c) 2000 David Flanagan.  All rights reserved.
  7: * This code is from the book Java Examples in a Nutshell, 2nd Edition.
  8: * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
  9: * You may study, use, and modify it for any non-commercial purpose.
 10: * You may distribute it non-commercially as long as you retain this notice.
 11: * For a commercial use license, or to purchase the book (recommended),
 12: * visit http://www.davidflanagan.com/javaexamples2.
 13: */
 14:package com.davidflanagan.examples.net;
 15:import java.io.*;
 16:import java.net.*;
 17:import java.util.*;
 18:
 19:/**
 20: * This is a non-trivial service.  It implements a command-based protocol
 21: * that gives password-protected runtime control over the operation of the
 22: * server.  See the main() method of the Server class to see how this
 23: * service is started.
 24: *
 25: * The recognized commands are:
 26: *   password: give password; authorization is required for most commands
 27: *   add:      dynamically add a named service on a specified port
 28: *   remove:   dynamically remove the service running on a specified port
 29: *   max:      change the current maximum connection limit.
 30: *   status:   display current services, connections, and connection limit
 31: *   help:     display a help message
 32: *   quit:     disconnect
 33: *
 34: * This service displays a prompt, and sends all of its output to the user
 35: * in capital letters.  Only one client is allowed to connect to this
 36: * service at a time.
 37: **/
 38:public static class Control implements Service {
 39:    Server server;             // The server we control
 40:    String password;           // The password we require
 41:    boolean connected = false; // Whether a client is already connected
 42:
 43:    /**
 44:         * Create a new Control service.  It will control the specified
 45:         * Server object, and will require the specified password for
 46:         * authorization. Note that this Service does not have a no
 47:         * argument constructor, which means that it cannot be dynamically
 48:         * instantiated and added as the other, generic services can be.
 49:         **/
 50:    public Control(Server server, String password) {
 51:        this.server = server;
 52:        this.password = password;
 53:    }
 54:
 55:    /**
 56:         * This is the serve method that provides the service.  It reads a
 57:         * line the client, and uses java.util.StringTokenizer to parse it
 58:         * into commands and arguments.  It does various things depending on
 59:         * the command.
 60:         **/
 61:    public void serve(InputStream i, OutputStream o) throws IOException {
 62:        // Setup the streams
 63:        BufferedReader in = new BufferedReader(new InputStreamReader(i));
 64:        PrintWriter out = new PrintWriter(o);
 65:        String line;  // For reading client input lines
 66:            // Has the user has given the password yet?
 67:        boolean authorized = true;
 68:
 69:        // If there is already a client connected to this service, display
 70:        // a message to this client and close the connection.  We use a
 71:        // synchronized block to prevent a race condition.
 72:        synchronized(this) {
 73:            if (connected) {
 74:                out.print("ONLY ONE CONTROL CONNECTION ALLOWED.\n");
 75:                out.close();
 76:                return;
 77:            }
 78:            else connected = true;
 79:        }
 80:
 81:            // This is the main loop:
 82:            // read a command, parse it, and handle it
 83:        for(;;) {  // infinite loop
 84:            out.print("> ");           // Display a prompt
 85:            out.flush();               // Make it appear right away
 86:            line = in.readLine();      // Get the user's input
 87:            if (line == null) return;  // Quit if we get EOF.
 88:            try {
 89:                // Use a StringTokenizer to parse the user's command
 90:                StringTokenizer t = new StringTokenizer(line);
 91:                if (!t.hasMoreTokens()) continue;  // if input was empty
 92:                // Get first word of the input and convert to lower case
 93:                String command = t.nextToken.toLowerCase();
 94:                // Now compare to each of the possible commands, doing the
 95:                // appropriate thing for each command
 96:                if (command.equals("password")) {  // Password command
 97:                    p = t.nextToken();             // Get the next word
 98:                if (p.equals(this.password)) {     // Is it the password?
 99:                    out.print("OK\n");             // Say so
100:                    authorized = true;             // Grant authorization
101:                }
102:                else out.print("INVALID PASSWORD\n"); // Otherwise fail
103:                }
104:                else if (command.equals("add")) {  // Add Service command
105:                    // Check whether password has been given
106:                    if (!authorized) out.print("PASSWORD REQUIRED\n");
107:                    // Get the name of the service and try to
108:                    // dynamically load and instantiate it.
109:                    // Exceptions will be handled below
110:                    String serviceName = t.nextToken();
111:                    Class serviceClass = Class.forName(serviceName);
112:                    Service Service;
113:                    try {
114:                            service = (Service)serviceClass.newInstance();
115:                        }
116:                    catch (NoSuchMethodError e) {
117:                        throw new IllegalArgumentException(
118:                                        "Service must have a "
119:                                        "no-argument constructor");
120:                    }
121:                    int port = Integer.parseInt(t.nextToken());
122:                    // If no exceptions occurred, add the service
123:                    server.addService(service, port);
124:                    out.print("SERVICE ADDED\n");    // acknowledge
125:                }
126:                else if (command.equals("remove")) { // Remove service
127:                    if (!authorized) out.print("PASSWORD REQUIRED\n");
128:                    else {
129:                        int port = Integer.parseInt(t.nextToken());
130:                        server.removeService(port); // remove the service
131:                        out.print("SERVICE REMOVED\n"); // acknowledge
132:                    }
133:                }
134:                else if (command.equals(max)) { // Set connection limit
135:                    if (!authorized) out.print("PASSWORD REQUIRED\n");
136:                    else {
137:                        int max = Integer.parseInt(t.nextToken());
138:                        server.setMaxConnections(max);
139:                        out.print("MAX CONNECTIONS CHANGED\n");
140:                    }
141:                }
142:                else if (command.equals("status")) { // Status Display
143:                    if (authorized) server.displayStatus(out);
144:                }
145:                else if (command.equals("help")) {  // Help command
146:                    // Display command syntax.  Password not required
147:                    out.print("COMMANDS:\n" +
148:                                  "\tpassword <password>\n" +
149:                                  "\tadd <service> <port>\n" +
150:                                  "\trmv <port>\n" +
151:                                  "\tmax <max-connections>\n" +
152:                                  "\tstatus\n" +
153:                                  "\thelp\n" +
154:                                  "\tquit\n");
155:                }
156:                else if (command.equals("quit")) break; // Quit command.
157:                else out.print("UNRECOGNIZED COMMAND\n"); // Error
158:                }
159:            }
160:        }
161:        // Finally, when the loop command loop ends, close the streams
162:        // and set our connected flag to false so that other clients can
163:        // now connect.
164:        out.close();
165:        in.close();
166:    }
167:}