| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.PrintStream; |
| import java.io.UnsupportedEncodingException; |
| import java.net.ServerSocket; |
| import java.net.Socket; |
| import java.nio.charset.StandardCharsets; |
| import java.text.SimpleDateFormat; |
| import java.util.Date; |
| import java.util.Enumeration; |
| import java.util.Vector; |
| |
| public class SimpleApplication implements Runnable { |
| |
| private ServerSocket server=null; |
| private Thread thread=null; |
| private volatile boolean stopping=false; |
| private String directory=null; |
| private final Vector<Handler> handlers; |
| |
| public static native void toto(); |
| |
| public SimpleApplication() |
| { |
| super(); |
| System.err.println("SimpleApplication: instance "+this.hashCode()+ |
| " created"); |
| this.handlers = new Vector<>(); |
| } |
| |
| @Override |
| protected void finalize() { |
| System.err.println("SimpleApplication: instance "+this.hashCode()+ |
| " garbage collected"); |
| } |
| |
| /** |
| * Main method |
| * |
| * @param args Optional port and directory configuration |
| * @throws Exception If the daemon cannot be set up |
| */ |
| public static void main(String[] args) throws Exception { |
| SimpleApplication app = new SimpleApplication(); |
| System.err.println("SimpleApplication: instance " + app.hashCode()+ |
| " init " + args.length); |
| int port=1200; |
| for (int i = 0; i < args.length; i++) { |
| System.err.println("SimpleApplication: arg " + i + |
| " = " + args[i]); |
| |
| } |
| if (args.length > 0 && !args[0].isEmpty()) |
| port=Integer.parseInt(args[0]); |
| if (args.length > 1) |
| app.directory = args[1]; |
| else |
| app.directory="/tmp"; |
| |
| // Dump a message |
| System.err.println("SimpleApplication: loading on port "+port); |
| Runtime.getRuntime().addShutdownHook(new ShutdownHook(app)); |
| // Set up this simple daemon |
| app.server = new ServerSocket(port); |
| app.thread = new Thread(app); |
| app.start(); |
| } |
| |
| public void start() |
| { |
| // Dump a message |
| System.err.println("SimpleApplication: starting"); |
| |
| // Start |
| this.thread.start(); |
| } |
| |
| public void stop() |
| throws IOException, InterruptedException |
| { |
| // Dump a message |
| System.err.println("SimpleApplication: stopping"); |
| |
| // Close the ServerSocket. This will make our thread to terminate |
| this.stopping=true; |
| this.server.close(); |
| |
| // Wait for the main thread to exit and dump a message |
| this.thread.join(5000); |
| System.err.println("SimpleApplication: stopped"); |
| } |
| |
| public void destroy() |
| { |
| System.err.println("SimpleApplication: instance "+this.hashCode()+ |
| " destroy"); |
| } |
| |
| @Override |
| public void run() |
| { |
| int number=0; |
| |
| System.err.println("SimpleApplication: started acceptor loop"); |
| try { |
| while (!this.stopping) { |
| Socket socket = this.server.accept(); |
| Handler handler = new Handler(socket, this); |
| handler.setConnectionNumber(number++); |
| handler.setDirectoryName(this.directory); |
| new Thread(handler).start(); |
| } |
| } catch (IOException e) { |
| // Don't dump any error message if we are stopping. A IOException |
| // is generated when the ServerSocket is closed in stop() |
| if (!this.stopping) e.printStackTrace(System.err); |
| } |
| |
| // Terminate all handlers that at this point are still open |
| Enumeration<Handler> openhandlers = this.handlers.elements(); |
| while (openhandlers.hasMoreElements()) { |
| Handler handler = openhandlers.nextElement(); |
| System.err.println("SimpleApplication: dropping connection "+ |
| handler.getConnectionNumber()); |
| handler.close(); |
| } |
| |
| System.err.println("SimpleApplication: exiting acceptor loop"); |
| } |
| |
| protected void addHandler(Handler handler) { |
| synchronized (handler) { |
| this.handlers.add(handler); |
| } |
| } |
| |
| protected void removeHandler(Handler handler) { |
| synchronized (handler) { |
| this.handlers.remove(handler); |
| } |
| } |
| |
| public static class ShutdownHook extends Thread |
| { |
| private final SimpleApplication instance; |
| |
| public ShutdownHook(SimpleApplication instance) |
| { |
| this.instance = instance; |
| } |
| @Override |
| public void run() |
| { |
| System.out.println("Shutting down"); |
| try { |
| instance.stop(); |
| } |
| catch (Exception e) { |
| e.printStackTrace(System.err); |
| } |
| } |
| } |
| |
| public static class Handler implements Runnable { |
| |
| private final SimpleApplication parent; |
| private String directory=null; // Only set before thread is started |
| private final Socket socket; |
| private int number=0; // Only set before thread is started |
| |
| public Handler(Socket s, SimpleApplication p) { |
| super(); |
| this.socket=s; |
| this.parent=p; |
| } |
| |
| @Override |
| public void run() { |
| this.parent.addHandler(this); |
| System.err.println("SimpleApplication: connection " + this.number + " opened from " + this.socket.getInetAddress()); |
| try { |
| InputStream in = this.socket.getInputStream(); |
| OutputStream out = this.socket.getOutputStream(); |
| handle(in, out); |
| this.socket.close(); |
| } catch (IOException e) { |
| e.printStackTrace(System.err); |
| } |
| System.err.println("SimpleApplication: connection " + this.number + " closed"); |
| this.parent.removeHandler(this); |
| } |
| |
| public void close() { |
| try { |
| this.socket.close(); |
| } catch (IOException e) { |
| e.printStackTrace(System.err); |
| } |
| } |
| |
| public void setConnectionNumber(int number) { |
| this.number=number; |
| } |
| |
| public int getConnectionNumber() { |
| return(this.number); |
| } |
| |
| public void setDirectoryName(String directory) { |
| this.directory=directory; |
| } |
| |
| public String getDirectoryName() { |
| return(this.directory); |
| } |
| |
| public void createFile(String name) throws IOException { |
| OutputStream file = new FileOutputStream(name, true); |
| PrintStream out = new PrintStream(file); |
| SimpleDateFormat fmt = new SimpleDateFormat(); |
| |
| out.println(fmt.format(new Date())); |
| out.close(); |
| file.close(); |
| } |
| |
| public void createDir(String name) |
| throws IOException { |
| File file = new File(name); |
| boolean ok = file.mkdirs(); |
| if (!ok) |
| throw new IOException("mkdirs for "+name+" failed"); |
| createFile(name); |
| } |
| |
| public void handle(InputStream in, OutputStream os) { |
| PrintStream out = null; |
| try { |
| out = new PrintStream(os, true, StandardCharsets.US_ASCII.name()); |
| } catch (UnsupportedEncodingException ex) { |
| out = new PrintStream(os, true); |
| } |
| |
| while (true) { |
| try { |
| // If we don't have data in the System InputStream, we want |
| // to ask to the user for an option. |
| if (in.available() == 0) { |
| out.println(); |
| out.println("Please select one of the following:"); |
| out.println(" 1) Shutdown"); |
| out.println(" 2) Create a file"); |
| out.println(" 3) Disconnect"); |
| out.println(" 4) Cause a core of the JVM"); |
| out.println(" 5) Create a directory"); |
| out.print("Your choice: "); |
| } |
| |
| // Read an option from the client |
| int x = in.read(); |
| |
| switch (x) { |
| // If the socket was closed, we simply return |
| case -1: |
| return; |
| |
| // Attempt to shutdown |
| case '1': |
| out.println("Attempting a shutdown..."); |
| try { |
| System.exit(0); |
| } catch (IllegalStateException e) { |
| out.println(); |
| out.println("Can't shutdown now"); |
| e.printStackTrace(out); |
| } |
| break; |
| |
| // Create a file |
| case '2': |
| String name = this.getDirectoryName() + "/SimpleApplication." + this.getConnectionNumber() + ".tmp"; |
| try { |
| this.createFile(name); |
| out.println("File '" + name + "' created"); |
| } catch (IOException e) { |
| e.printStackTrace(out); |
| } |
| break; |
| |
| // Disconnect |
| case '3': |
| out.println("Disconnecting..."); |
| return; |
| |
| // Crash JVM in a native call: It need an so file ;-) |
| case '4': |
| System.load(System.getProperty("native.library", "./Native.so")); |
| toto(); |
| break; |
| |
| // Create a directory (PR 30177 with 1.4.x and 1.5.0 |
| case '5': |
| String name1 = this.getDirectoryName() + "/a/b/c/d/e/SimpleApplication." + this.getConnectionNumber() + ".tmp"; |
| try { |
| this.createDir(name1); |
| out.println("File '" + name1 + "' created"); |
| } catch (IOException e) { |
| e.printStackTrace(out); |
| } |
| break; |
| |
| // Discard any carriage return / newline characters |
| case '\r': |
| case '\n': |
| break; |
| |
| // We got something that we weren't supposed to get |
| default: |
| out.println("Unknown option '" + (char) x + "'"); |
| break; |
| |
| } |
| |
| // If we get an IOException we return (disconnect) |
| } catch (IOException e) { |
| System.err.println("SimpleApplication: IOException in connection " + this.getConnectionNumber()); |
| return; |
| } |
| } |
| } |
| } |
| } |