package org.neo4j.util.shell.apps.extra;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import org.neo4j.util.shell.AppCommandParser;
import org.neo4j.util.shell.Output;
import org.neo4j.util.shell.Session;
import org.neo4j.util.shell.ShellException;
public class GshExecutor
{
public static final String PATH_STRING = "GSH_PATH";
public static final String BINDING_CLASS = "groovy.lang.Binding";
public static final String ENGINE_CLASS = "groovy.util.GroovyScriptEngine";
public void execute( String line, Session session, Output out )
throws ShellException
{
this.ensureGroovyIsInClasspath();
if ( line == null || line.trim().length() == 0 )
{
throw new ShellException( "Need to supply groovy scripts" );
}
List<String> pathList = this.getEnvPaths( session );
Object groovyScriptEngine = this.newGroovyScriptEngine(
pathList.toArray( new String[ pathList.size() ] ) );
Map<String, Object> properties = new HashMap<String, Object>();
properties.put( "out", new GshOutput( out ) );
properties.put( "session", session );
this.runGroovyScripts( groovyScriptEngine, properties, line );
}
private void runGroovyScripts( Object groovyScriptEngine,
Map<String, Object> properties, String line ) throws ShellException
{
ArgReader reader = new ArgReader(
AppCommandParser.tokenizeStringWithQuotes( line ) );
HashMap<String, Object> hashMap =
( HashMap<String, Object> ) properties;
while ( reader.hasNext() )
{
String arg = reader.next();
if ( arg.startsWith( "--" ) )
{
String[] scriptArgs = getScriptArgs( reader );
String scriptName = arg.substring( 2 );
Map<String, Object> props =
( Map<String, Object> ) hashMap.clone();
props.put( "args", scriptArgs );
this.runGroovyScript( groovyScriptEngine, scriptName,
this.newGroovyBinding( props ) );
}
}
}
private void runGroovyScript( Object groovyScriptEngine,
String scriptName, Object groovyBinding ) throws ShellException
{
try
{
Method runMethod = groovyScriptEngine.getClass().getMethod(
"run", String.class, groovyBinding.getClass() );
runMethod.invoke( groovyScriptEngine, scriptName + ".groovy",
groovyBinding );
}
catch ( Exception e )
{
// Don't pass the exception on because the client most certainly
// doesn't have groovy in the classpath.
throw new ShellException( "Groovy exception: " +
this.findProperMessage( e ) );
}
}
private String findProperMessage( Throwable e )
{
String message = e.getMessage();
if ( e.getCause() != null )
{
message = this.findProperMessage( e.getCause() );
}
return message;
}
private String[] getScriptArgs( ArgReader reader )
{
reader.mark();
try
{
ArrayList<String> list = new ArrayList<String>();
while ( reader.hasNext() )
{
String arg = reader.next();
if ( arg.startsWith( "--" ) )
{
break;
}
list.add( arg );
reader.mark();
}
return list.toArray( new String[ list.size() ] );
}
finally
{
reader.flip();
}
}
private List<String> getEnvPaths( Session session )
throws ShellException
{
try
{
List<String> list = new ArrayList<String>();
collectPaths( list, ( String ) session.get( PATH_STRING ) );
collectPaths( list, Gsh.DEFAULT_PATHS );
return list;
}
catch ( RemoteException e )
{
throw new ShellException( e );
}
}
private void collectPaths( List<String> paths, String pathString )
{
if ( pathString != null && pathString.trim().length() > 0 )
{
for ( String path : pathString.split( ":" ) )
{
paths.add( path );
}
}
}
private Object newGroovyBinding( Map<String, Object> properties )
throws ShellException
{
try
{
Class cls = Class.forName( BINDING_CLASS );
Object binding = cls.newInstance();
Method setPropertyMethod =
cls.getMethod( "setProperty", String.class, Object.class );
for ( String key : properties.keySet() )
{
setPropertyMethod.invoke( binding, key, properties.get( key ) );
}
return binding;
}
catch ( Exception e )
{
throw new ShellException( "Invalid groovy classes", e );
}
}
private Object newGroovyScriptEngine( String[] paths )
throws ShellException
{
try
{
Class cls = Class.forName( ENGINE_CLASS );
return cls.getConstructor( String[].class ).newInstance(
new Object[] { paths } );
}
catch ( Exception e )
{
throw new ShellException( "Invalid groovy classes", e );
}
}
private void ensureGroovyIsInClasspath() throws ShellException
{
try
{
Class.forName( BINDING_CLASS );
}
catch ( ClassNotFoundException e )
{
throw new ShellException( "Groovy couldn't be found", e );
}
}
private static class ArgReader implements Iterator<String>
{
private static final int START_INDEX = -1;
private int index = START_INDEX;
private String[] args;
private Integer mark;
ArgReader( String[] args )
{
this.args = args;
}
public boolean hasNext()
{
return this.index + 1 < this.args.length;
}
public String next()
{
if ( !hasNext() )
{
throw new NoSuchElementException();
}
this.index++;
return this.args[ this.index ];
}
public void previous()
{
this.index--;
if ( this.index < START_INDEX )
{
this.index = START_INDEX;
}
}
public void remove()
{
throw new UnsupportedOperationException();
}
public void mark()
{
this.mark = this.index;
}
public void flip()
{
if ( this.mark == null )
{
throw new IllegalStateException();
}
this.index = this.mark;
this.mark = null;
}
}
public static class GshOutput implements Output
{
private Output source;
GshOutput( Output output )
{
this.source = output;
}
public void print( Serializable object ) throws RemoteException
{
source.print( object );
}
public void println( Serializable object ) throws RemoteException
{
source.println( object );
}
public Appendable append( char c ) throws IOException
{
return source.append( c );
}
public Appendable append( CharSequence csq, int start, int end )
throws IOException
{
return source.append( csq, start, end );
}
public Appendable append( CharSequence csq ) throws IOException
{
return source.append( csq );
}
public void print( Object object ) throws RemoteException
{
source.print( object.toString() );
}
public void println() throws RemoteException
{
source.println();
}
public void println( Object object ) throws RemoteException
{
source.println( object.toString() );
}
}
}