package TcpExamples; import java.io.*; import java.net.*; import java.util.Arrays; import java.util.List; /** * <p> * This utility class supports the {@link TcpExamples.TcpSentryDispatchServer} program, * handling all programming logic needed for a new socket connection * to run in a thread of its own. This is the server * portion as well, so we artificially invent what happens * if the server can't respond to a connection for several seconds. * </p> * <p> * Warning: do not run this class! It is created and used automatically by {@link TcpExamples.TcpSentryDispatchServer} at run time. * </p> * * @see TcpSentryClient * @see TcpSentryDispatchServer * @see TcpExample4Client * @see TcpExample4DispatchServer * @see TcpExample4HandlerThread * * @see <a href="../../../src/TcpExamples/TcpSentryTerminalLog.txt" target="blank">TcpSentryTerminalLog.txt</a> * @see <a href="../../../src/TcpExamples/TcpExample4SequenceDiagram.png" target="blank">TcpExample4SequenceDiagram.png</a> * @see <a href="../../../src/TcpExamples/TcpExample4SequenceSketch.png" target="blank">TcpExample4SequenceSketch.png</a> * * @author Don McGregor * @author Don Brutzman * @author MV3500 class */ public class TcpSentryHandlerThread extends Thread { /** who is speaking, by role */ static final String ACTOR = "[sentry] "; /** Sentry Scenario access list, as written this list is case sensitive. * See <a href="https://stackoverflow.com/questions/1005073/initialization-of-an-arraylist-in-one-line" target="blank">https://stackoverflow.com/questions/1005073/initialization-of-an-arraylist-in-one-line</a> */ public static List<String> allowedNamesList = Arrays.asList("Tim", "Stephen", "Mark", "Rene", "Simon", "James", "Ethan", "Jin Hong", "Don"); /** Sentry command vocabulary: the guard is out of the house! */ public static final String SENTRY_WATCH_PRESENT = "Announcement: the sentry watch is present and standing guard."; /** Sentry command vocabulary: hand signal or loud statement (across the standoff distance) to next vehicle waiting at STOP sign */ public static final String APPROACH_THE_GATE = "(verbal or hand gesture) You may approach the gate."; /** Sentry command vocabulary: use a standard admonition for broad recognition by visitors */ public static final String HALT_WHO_GOES_THERE = "Halt who goes there?"; /** Sentry command vocabulary */ public static final String YOU_MAY_PASS = "You may pass, have a great MOVES day!"; /** Sentry command vocabulary */ public static final String HOLD_IT_RIGHT_THERE = "You may not pass! Leave immediately or else..."; /** Sentry command vocabulary */ public static final String PAY_ATTENTION = "Did you hear me? Pay attention please."; /** TODO, not implemented: visitor ignores the sentry's direction to leave and enters the base */ public static final String INTRUDER_ALERT = "An unauthorized visitor has entered the base!"; /** The socket connection to a client */ Socket socket; /** * The thread constructor creates the socket from a ServerSocket, waiting for the client to connect, * and passes that socket when constructing the thread responsible for handling the connection. * * @param socket The socket connection handled by this thread */ TcpSentryHandlerThread(Socket socket) { this.socket = socket; System.out.println(ACTOR + "1h.TcpSentryClient, " + TcpSentryHandlerThread.class.getName()); } /** * Program invocation and execution starts here - but is illegal and unwanted, so warn the unsuspecting user! * @param args command-line arguments */ public static void main(String[] args) { System.out.println ("*** TcpSentryHandlerThread is not a standalone executable progam."); System.out.println ("*** Please run TcpSentryDispatchServer instead... now handing the baton back to the boss."); TcpSentryDispatchServer.main(new String[] { /* dummy args */ }); // exit } /** Handles one connection. We add an artificial slowness * to handling the connection with a sleep(). This means * the client won't see a server connection response for ten seconds (default). */ // @overriding run() method in Java Thread class is deliberate @Override public void run() { try { System.out.println(ACTOR + "startup, menu 1g.TcpSentryServer, " + TcpSentryHandlerThread.class.getName()); // now starting to handle the thread // setup input stream and output stream InputStream socketInputStream = socket.getInputStream(); Reader socketInputStreamReader = new InputStreamReader(socketInputStream); BufferedReader socketBufferedReader = new BufferedReader(socketInputStreamReader); OutputStream socketOutputStream = socket.getOutputStream(); PrintStream socketPrintStream = new PrintStream(socketOutputStream); final long TIMEOUT = 100; // 2000 milliseconds = 2 seconds, 10000 milliseconds = 10 seconds // System.out.println(prefix + " pausing for TIMEOUT=" + TIMEOUT + "ms" + // " to emulate computation and avoid server-side overload"); Thread.sleep(TIMEOUT); //////////////////////////////////////////////////////////////////////////////////////////// // Assignment code // since this thread is running, a socket connection has already been received from dispatch server. // PrintStream is the Java way to use System.print() to pass string data along the socket. socketPrintStream.println(ACTOR + SENTRY_WATCH_PRESENT); System.out.println(ACTOR + SENTRY_WATCH_PRESENT); // make sure that message indeed escapes current process and is pushed through socket to reach the client socketPrintStream.flush(); // now the query-response interactions begin... socketPrintStream.println(ACTOR + APPROACH_THE_GATE); System.out.println(ACTOR + APPROACH_THE_GATE); // No communications response occurs, instead the visitor drives up to the gate socketPrintStream.println(ACTOR + HALT_WHO_GOES_THERE); System.out.println(ACTOR + HALT_WHO_GOES_THERE); String clientResponse = socketBufferedReader.readLine(); System.out.println (clientResponse); // trim actor prefix if neccessary int messageIndex = clientResponse.indexOf("]"); if (messageIndex > 0) clientResponse = clientResponse.substring(messageIndex + 1).trim(); while (clientResponse.isBlank()) // repeat if needed { socketPrintStream.println(ACTOR + PAY_ATTENTION); System.out.println(ACTOR + PAY_ATTENTION); socketPrintStream.println(ACTOR + HALT_WHO_GOES_THERE); System.out.println(ACTOR + HALT_WHO_GOES_THERE); clientResponse = socketBufferedReader.readLine(); System.out.println (clientResponse); // trim actor prefix if neccessary messageIndex = clientResponse.indexOf("]"); if (messageIndex > 0) clientResponse = clientResponse.substring(messageIndex + 1).trim(); } if (clientResponse.trim().equalsIgnoreCase("quit") || clientResponse.trim().equalsIgnoreCase("exit")) { System.out.println (ACTOR + "Exiting the program."); socket.close(); System.exit(0); // shuts down thread, not dispatch server } // simple response socketPrintStream.println(ACTOR + "Hello, " + clientResponse); System.out.println(ACTOR + "Hello, " + clientResponse); // now check credential and respond accordingly if (allowedNamesList.contains(clientResponse)) { socketPrintStream.println(ACTOR + YOU_MAY_PASS); System.out.println(ACTOR + YOU_MAY_PASS); } else { socketPrintStream.println(ACTOR + HOLD_IT_RIGHT_THERE); System.out.println(ACTOR + HOLD_IT_RIGHT_THERE); } //////////////////////////////////////////////////////////////////////////////////////////// // socket.close(); // all clear, no longer need socket System.out.println(ACTOR + "this visitor interaction is complete."); System.out.println("================================================================================="); // execution complete } catch(IOException | InterruptedException e) // either a networking or a threading problem { System.out.println("*** " + ACTOR + "Problem with networking,"); // describe what is happening System.out.println(" " + e); // Provide more helpful information to user if exception occurs due to running twice at one time if (e instanceof java.net.BindException) System.out.println("*** Be sure to stop any other running instances of programs using this port!"); } finally { System.out.println(TcpSentryHandlerThread.class.getName() + " all done."); } } }