All Downloads are FREE. Search and download functionalities are using the official Maven repository.

groovy.ui.GroovySocketServer Maven / Gradle / Ivy

There is a newer version: 5.0.0-alpha-11
Show newest version
/*
 *  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
 *
 *    http://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.
 */
package groovy.ui;

import groovy.lang.GroovyCodeSource;
import groovy.lang.GroovyRuntimeException;
import groovy.lang.GroovyShell;
import groovy.lang.Script;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.regex.Pattern;

/**
 * Simple server that executes supplied script against a socket.
 * 

* Typically this is used from the groovy command line agent but it can be * invoked programmatically. To run this program from the command line please * refer to the command line documentation at * * Running Groovy from the commandline. *

* Here is an example of how to use this class to open a listening socket on the server, * listen for incoming data, and then echo the data back to the client in reverse order: *

 * new GroovySocketServer(
 *         new GroovyShell(),      // evaluator
 *         false,                  // is not a file
 *         "println line.reverse()",         // script to evaluate
 *         true,                   // return result to client
 *         1960)                   //port
 * 
* There are several variables in the script binding: *
    *
  • line - The data from the socket
  • *
  • out - The output PrintWriter, should you need it for some reason.
  • *
  • socket - The socket, should you need it for some reason.
  • *
*/ public class GroovySocketServer implements Runnable { private URL url; private final GroovyShell groovy; private final GroovyCodeSource source; private final boolean autoOutput; private static int counter; /** * This creates and starts the socket server on a new Thread. There is no need to call run or spawn * a new thread yourself. * @param groovy * The GroovyShell object that evaluates the incoming text. If you need additional classes in the * classloader then configure that through this object. * @param isScriptFile * Whether the incoming socket data String will be a script or a file path. * @param scriptFilenameOrText * This will be a groovy script or a file location depending on the argument isScriptFile. * @param autoOutput * whether output should be automatically echoed back to the client * @param port * the port to listen on * */ public GroovySocketServer(GroovyShell groovy, boolean isScriptFile, String scriptFilenameOrText, boolean autoOutput, int port) { this(groovy, getCodeSource(isScriptFile, scriptFilenameOrText), autoOutput, port); } private static GroovyCodeSource getCodeSource(boolean scriptFile, String scriptFilenameOrText) { if (scriptFile) { try { if (URI_PATTERN.matcher(scriptFilenameOrText).matches()) { return new GroovyCodeSource(new URI(scriptFilenameOrText)); } else { return new GroovyCodeSource(GroovyMain.searchForGroovyScriptFile(scriptFilenameOrText)); } } catch (IOException e) { throw new GroovyRuntimeException("Unable to get script from: " + scriptFilenameOrText, e); } catch (URISyntaxException e) { throw new GroovyRuntimeException("Unable to get script from URI: " + scriptFilenameOrText, e); } } else { // We could jump through some hoops to have GroovyShell make our script name, but that seems unwarranted. // If we *did* jump through that hoop then we should probably change the run loop to not recompile // the script on every iteration since the script text can't change (the reason for the recompilation). return new GroovyCodeSource(scriptFilenameOrText, generateScriptName(), GroovyShell.DEFAULT_CODE_BASE); } } private static synchronized String generateScriptName() { return "ServerSocketScript" + (++counter) + ".groovy"; } // RFC2396 // scheme = alpha *( alpha | digit | "+" | "-" | "." ) private static final Pattern URI_PATTERN = Pattern.compile("\\p{Alpha}[-+.\\p{Alnum}]*:.*"); /** * This creates and starts the socket server on a new Thread. There is no need to call run or spawn * a new thread yourself. * @param groovy * The GroovyShell object that evaluates the incoming text. If you need additional classes in the * classloader then configure that through this object. * @param source * GroovyCodeSource for the Groovy script * @param autoOutput * whether output should be automatically echoed back to the client * @param port * the port to listen on * @since 2.3.0 */ public GroovySocketServer(GroovyShell groovy, GroovyCodeSource source, boolean autoOutput, int port) { this.groovy = groovy; this.source = source; this.autoOutput = autoOutput; try { url = new URL("http", InetAddress.getLocalHost().getHostAddress(), port, "/"); System.out.println("groovy is listening on port " + port); } catch (IOException e) { e.printStackTrace(); } new Thread(this).start(); } /** * Runs this server. There is typically no need to call this method, as the object's constructor * creates a new thread and runs this object automatically. */ @Override public void run() { try (ServerSocket serverSocket = new ServerSocket(url.getPort())) { while (true) { // Create one script per socket connection. // This is purposefully not caching the Script // so that the script source file can be changed on the fly, // as each connection is made to the server. //FIXME: Groovy has other mechanisms specifically for watching to see if source code changes. // We should probably be using that here. // See also the comment about the fact we recompile a script that can't change. Script script = groovy.parse(source); new GroovyClientConnection(script, autoOutput, serverSocket.accept()); } } catch (Exception e) { e.printStackTrace(); } } static class GroovyClientConnection implements Runnable { private Script script; private Socket socket; private BufferedReader reader; private PrintWriter writer; private boolean autoOutputFlag; GroovyClientConnection(Script script, boolean autoOutput,Socket socket) throws IOException { this.script = script; this.autoOutputFlag = autoOutput; this.socket = socket; reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); writer = new PrintWriter(socket.getOutputStream()); new Thread(this, "Groovy client connection - " + socket.getInetAddress().getHostAddress()).start(); } @Override public void run() { try { String line = null; script.setProperty("out", writer); script.setProperty("socket", socket); script.setProperty("init", Boolean.TRUE); while ((line = reader.readLine()) != null) { // System.out.println(line); script.setProperty("line", line); Object o = script.run(); script.setProperty("init", Boolean.FALSE); if (o != null) { if ("success".equals(o)) { break; // to close sockets gracefully etc... } else { if (autoOutputFlag) { writer.println(o); } } } writer.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { try { writer.flush(); writer.close(); } finally { try { socket.close(); } catch (IOException e3) { e3.printStackTrace(); } } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy