Tiny GroovyServ

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

Projeto no github.

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()
    }
    
}

Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*


nove − 4 =

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>