/* * 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 org.codehaus.groovy.runtime; import groovy.lang.Closure; import groovy.transform.stc.ClosureParams; import groovy.transform.stc.SimpleType; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Writer; import java.net.ServerSocket; import java.net.Socket; import java.util.logging.Logger; /** * This class defines new groovy methods for Sockets which enhance * JDK classes inside the Groovy environment. * <p> * NOTE: While this class contains many 'public' static methods, it is * primarily regarded as an internal class (its internal package name * suggests this also). We value backwards compatibility of these * methods when used within Groovy but value less backwards compatibility * at the Java method call level. I.e. future versions of Groovy may * remove or move a method call in this file but would normally * aim to keep the method available from within Groovy. */ public class SocketGroovyMethods extends DefaultGroovyMethodsSupport { private static final Logger LOG = Logger.getLogger(SocketGroovyMethods.class.getName()); /** * Passes the Socket's InputStream and OutputStream to the closure. The * streams will be closed after the closure returns, even if an exception * is thrown. * * @param socket a Socket * @param closure a Closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withStreams(Socket socket, @ClosureParams(value=SimpleType.class, options={"java.io.InputStream","java.io.OutputStream"}) Closure<T> closure) throws IOException { InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream(); try { T result = closure.call(new Object[]{input, output}); InputStream temp1 = input; input = null; temp1.close(); OutputStream temp2 = output; output = null; temp2.close(); return result; } finally { closeWithWarning(input); closeWithWarning(output); } } /** * Creates an InputObjectStream and an OutputObjectStream from a Socket, and * passes them to the closure. The streams will be closed after the closure * returns, even if an exception is thrown. * * @param socket this Socket * @param closure a Closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static <T> T withObjectStreams(Socket socket, @ClosureParams(value=SimpleType.class, options={"java.io.ObjectInputStream","java.io.ObjectOutputStream"}) Closure<T> closure) throws IOException { InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(output); ObjectInputStream ois = new ObjectInputStream(input); try { T result = closure.call(new Object[]{ois, oos}); InputStream temp1 = ois; ois = null; temp1.close(); temp1 = input; input = null; temp1.close(); OutputStream temp2 = oos; oos = null; temp2.close(); temp2 = output; output = null; temp2.close(); return result; } finally { closeWithWarning(ois); closeWithWarning(input); closeWithWarning(oos); closeWithWarning(output); } } /** * Overloads the left shift operator to provide an append mechanism to * add things to the output stream of a socket * * @param self a Socket * @param value a value to append * @return a Writer * @throws IOException if an IOException occurs. * @since 1.0 */ public static Writer leftShift(Socket self, Object value) throws IOException { return IOGroovyMethods.leftShift(self.getOutputStream(), value); } /** * Overloads the left shift operator to provide an append mechanism * to add bytes to the output stream of a socket * * @param self a Socket * @param value a value to append * @return an OutputStream * @throws IOException if an IOException occurs. * @since 1.0 */ public static OutputStream leftShift(Socket self, byte[] value) throws IOException { return IOGroovyMethods.leftShift(self.getOutputStream(), value); } /** * Accepts a connection and passes the resulting Socket to the closure * which runs in a new Thread. * * @param serverSocket a ServerSocket * @param closure a Closure * @return a Socket * @throws IOException if an IOException occurs. * @see java.net.ServerSocket#accept() * @since 1.0 */ public static Socket accept(ServerSocket serverSocket, @ClosureParams(value=SimpleType.class, options="java.net.Socket") final Closure closure) throws IOException { return accept(serverSocket, true, closure); } /** * Accepts a connection and passes the resulting Socket to the closure * which runs in a new Thread or the calling thread, as needed. * * @param serverSocket a ServerSocket * @param runInANewThread This flag should be true, if the closure should be invoked in a new thread, else false. * @param closure a Closure * @return a Socket * @throws IOException if an IOException occurs. * @see java.net.ServerSocket#accept() * @since 1.7.6 */ public static Socket accept(ServerSocket serverSocket, final boolean runInANewThread, @ClosureParams(value=SimpleType.class, options="java.net.Socket") final Closure closure) throws IOException { final Socket socket = serverSocket.accept(); if (runInANewThread) { new Thread(new Runnable() { public void run() { invokeClosureWithSocket(socket, closure); } }).start(); } else { invokeClosureWithSocket(socket, closure); } return socket; } private static void invokeClosureWithSocket(Socket socket, Closure closure) { try { closure.call(socket); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { LOG.warning("Caught exception closing socket: " + e); } } } } }