package org.neo4j.util.shell.apps.extra; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; 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; /** * Executes groovy scripts purely via reflection */ public abstract class ScriptExecutor { protected abstract String getPathKey(); protected String getDefaultPaths() { return ".:src:src" + File.separator + "script"; } /** * Executes a groovy script (with arguments) defined in {@code line}. * @param line the line which defines the groovy script with arguments. * @param session the {@link Session} to include as argument in groovy. * @param out the {@link Output} to include as argument in groovy. * @throws ShellException if the execution of a groovy script fails. */ public void execute( String line, Session session, Output out ) throws ShellException { this.ensureDependenciesAreInClasspath(); if ( line == null || line.trim().length() == 0 ) { return; } List<String> pathList = this.getEnvPaths( session ); String[] paths = pathList.toArray( new String[ pathList.size() ] ); Object interpreter = this.newInterpreter( paths ); Map<String, Object> properties = new HashMap<String, Object>(); properties.put( "out", out ); properties.put( "session", session ); this.runScripts( interpreter, properties, line, paths ); } private void runScripts( Object interpreter, Map<String, Object> properties, String line, String[] paths ) throws ShellException { ArgReader reader = new ArgReader( AppCommandParser.tokenizeStringWithQuotes( line ) ); while ( reader.hasNext() ) { String arg = reader.next(); if ( arg.startsWith( "--" ) ) { String[] scriptArgs = getScriptArgs( reader ); String scriptName = arg.substring( 2 ); Map<String, Object> props = new HashMap<String, Object>( properties ); props.put( "args", scriptArgs ); this.runScript( interpreter, scriptName, props, paths ); } } } protected abstract void runScript( Object interpreter, String scriptName, Map<String, Object> properties, String[] paths ) throws ShellException; protected String findProperMessage( Throwable e ) { String message = e.toString(); if ( e.getCause() != null ) { message = this.findProperMessage( e.getCause() ); } return message; } protected String stackTraceAsString( Throwable e ) { StringWriter writer = new StringWriter(); PrintWriter printer = new PrintWriter( writer ); e.printStackTrace( printer ); printer.close(); return writer.getBuffer().toString(); } 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( getPathKey() ) ); collectPaths( list, getDefaultPaths() ); 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 ); } } } protected abstract Object newInterpreter( String[] paths ) throws ShellException; protected abstract void ensureDependenciesAreInClasspath() throws ShellException; public 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 ]; } /** * Goes to the previous argument. */ public void previous() { this.index--; if ( this.index < START_INDEX ) { this.index = START_INDEX; } } public void remove() { throw new UnsupportedOperationException(); } /** * Marks the position so that a call to {@link #flip()} returns to that * position. */ public void mark() { this.mark = this.index; } /** * Flips back to the position defined in {@link #mark()}. */ public void flip() { if ( this.mark == null ) { throw new IllegalStateException(); } this.index = this.mark; this.mark = null; } } }