GroovyServ é uma solução interessante para a execução de múltiplos scripts Groovy em uma JVM compartilhada. Infelizmente, conforme citado nesse tópico, a limitação em relação aos sistemas operacionais suportados pode se tornar um problema.
Como abordagem alternativa, foi escrito um pequeno servidor SSH usando Groovy. Dessa maneira, qualquer cliente SSH nativo estaria apto a enviar comandos de execução de script ao servidor. A idéia segue a mesma linha de raciocínio da combinação ‘groovy -l’ e telnet. Para todo o tratamento do protocolo SSH, foi utilizado o Apache Mina SSHD.
Vale ressaltar que o servidor desenvolvido é apenas um primo distante, não otimizado, inseguro e funcionalmente limitado do GroovyServ, não tendo a pretensão de substituí-lo. Para a execução em ambientes não controlados, aconselha-se a utilização de um SecurityManager devidamente configurado, bem como a adoção de mecanismo de autenticação adequado.
Passo-a-passo para a utilização:
1. Efetuar download do código-fonte do servidor
2. Descompactar o conteúdo do arquivo em um diretório
3. Iniciar o servidor
[user@techdm tiny-groovyserv]$ groovy tgserv.groovy
ou
[user@techdm tiny-groovyserv]$ groovy tgserv.groovy 8123
4. Escrever alguns scripts no mesmo computador
5. Iniciar mais de prompt de comando ou shell e executar, simultaneamente, os scripts criados
[user@techdm test]$ ssh -p 8123 localhost /tmp/test/a.groovy abc 123
[user@techdm dir1]$ ssh -p 8123 localhost /tmp/test/dir1/b.groovy
[user@techdm dir2]$ ssh -p 8123 localhost /tmp/test/dir2/c.groovy
ou usando plink:
C:\tmp\test> plink -P 8123 -ssh -l x localhost c:/tmp/test/a.groovy abc 123
C:\tmp\test\dir1> plink -P 8123 -ssh -l x localhost c:/tmp/test/dir1/b.groovy
C:\tmp\test\dir2> plink -P 8123 -ssh -l x localhost c:/tmp/test/dir2/c.groovy
Referências adicionais:
/* * http://www.apache.org/licenses/LICENSE-2.0 */ /** * @author Daniel Henrique Alves Lima */ import java.io.IOException; import groovy.lang.GroovyClassLoader import org.codehaus.groovy.runtime.StackTraceUtils import org.apache.sshd.SshServer import org.apache.sshd.server.auth.UserAuthNone import org.apache.sshd.server.Command import org.apache.sshd.server.CommandFactory import org.apache.sshd.server.command.ScpCommandFactory import org.apache.sshd.server.Environment import org.apache.sshd.server.ExitCallback import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider //import org.apache.sshd.server.PasswordAuthenticator //import org.apache.sshd.server.PublickeyAuthenticator SshServer sshd = SshServer.setUpDefaultServer() sshd.port = args && args.length > 0? args[0] as int : 8123 sshd.keyPairProvider = new SimpleGeneratorHostKeyProvider('hostkey.ser') def sysOut = System.out def findMyClassLoader = {ClassLoader cl = Thread.currentThread().contextClassLoader -> while (cl != null && !(cl instanceof MyGroovyClassLoader)) { cl = cl.parent } return cl } sshd.commandFactory = new ScpCommandFactory( {command -> command = command.tokenize(' ') def cmd cmd = [ input: null, output: null, error:null, callback: null, setExitCallback: {cb -> cmd.callback = cb}, setInputStream: {is -> cmd.input = is as InputStream}, setOutputStream: {out -> cmd.output = new PrintStream(out)}, setErrorStream: {err -> cmd.error = new PrintStream(err)}, destroy: {}, start: {env -> Thread currentThread = Thread.currentThread() sysOut.println "${currentThread} begin" def oldThreadCl = currentThread.contextClassLoader def cl = new MyGroovyClassLoader(oldThreadCl? oldThreadCl : this.class.classLoader) cl.errorStream = cmd.error; cl.inputStream = cmd.input; cl.outputStream = cmd.output def scriptName = command.size() > 0?"${command[0]}":'' def scriptArgs = (command.size() > 1? command.subList(1, command.size()): []) as String[] ThreadGroup tg = new ThreadGroup(currentThread.threadGroup, "${currentThread.threadGroup.name}:${scriptName}") Thread t = new Thread(tg, { long time = -1; int result = -1 try { def scriptFile = new File(scriptName) if (scriptFile.parentFile) {cl.addURL(scriptFile.parentFile.toURI().toURL())} def script = cl.parseClass(scriptFile) assert cl.equals(findMyClassLoader(script.classLoader)) time = System.currentTimeMillis() script = script.newInstance() script.args = scriptArgs; script.run() result = 0 } catch (Exception e) { e = StackTraceUtils.deepSanitize(e) sshd.log.error("Error running script ${scriptName}", e) e.printStackTrace() throw e } finally { try { while (tg.activeCount() > 1 || tg.activeGroupCount() > 1) {Thread.sleep 100} System.err.flush(); System.out.flush() cl.release() } finally { if (time > 0) {time = System.currentTimeMillis() - time; sysOut.println "${Thread.currentThread()} ${time} (ms)"} sysOut.println "${Thread.currentThread()} end" cmd.callback.onExit(result) } } } as Runnable ) t.contextClassLoader = cl t.start() } ] return cmd as Command } as CommandFactory ) /*sshd.publickeyAuthenticator = {username, key, session -> return true } as PublickeyAuthenticator*/ /*sshd.passwordAuthenticator = {username, password, session -> return true } as PasswordAuthenticator*/ def userAuthFactories = sshd.userAuthFactories if (!userAuthFactories) {userAuthFactories = []} userAuthFactories << new UserAuthNone.Factory() sshd.userAuthFactories = userAuthFactories System.err = new PrintStream(new MyOutputStream(System.err, findMyClassLoader)) System.out = new PrintStream(new MyOutputStream(System.out, findMyClassLoader)) System.in = new MyInputStream(System.in, findMyClassLoader) sshd.start() class MyGroovyClassLoader extends GroovyClassLoader { def inputStream def outputStream def errorStream public MyGroovyClassLoader() { super() } public MyGroovyClassLoader(ClassLoader loader) { super(loader) } public MyGroovyClassLoader(GroovyClassLoader parent) { super(parent) } public void release() { errorStream?.flush(); outputStream?.flush() errorStream?.close(); outputStream?.close(); inputStream?.close() } } class MyInputStream extends InputStream { private InputStream input private Closure selectCl public MyInputStream(InputStream input, Closure selectCl) {this.input = input; this.selectCl = selectCl} @Override public int read() throws IOException { def cl = selectCl() return (cl? cl.inputStream : input).read() } @Override public int read(byte[] b, int off, int len) throws IOException { def cl = selectCl() return (cl? cl.inputStream : input).read(b, off, len) } } class MyOutputStream extends OutputStream { private OutputStream output private Closure selectCl public MyOutputStream(OutputStream output, Closure selectCl) {this.output = output; this.selectCl = selectCl} @Override public void write(int b) throws IOException { def cl = selectCl() (cl? cl.outputStream : output).write(b) } @Override public void write(byte[] b, int off, int len) throws IOException { def cl = selectCl() (cl? cl.outputStream : output).write(b, off, len) flush() // } @Override public void flush() throws IOException { def cl = selectCl() (cl? cl.outputStream : output).flush() } }