1414import java .net .URL ;
1515import java .util .ArrayList ;
1616import java .util .List ;
17+ import java .util .regex .Matcher ;
18+ import java .util .regex .Pattern ;
19+
1720import org .eclipse .core .runtime .FileLocator ;
1821import org .eclipse .core .runtime .Path ;
1922import org .eclipse .core .runtime .Platform ;
@@ -51,6 +54,7 @@ public class RubyScriptNodeModel extends NodeModel {
5154 public static final String APPEND_COLS = "append_columns" ;
5255 public static final String COLUMN_NAMES = "new_column_names" ;
5356 public static final String COLUMN_TYPES = "new_column_types" ;
57+
5458 protected int numInputs = 0 ;
5559 protected int numOutputs = 0 ;
5660
@@ -72,17 +76,43 @@ public class RubyScriptNodeModel extends NodeModel {
7276
7377 private boolean snippetMode ;
7478
79+ private class ScriptError {
80+ public int errorLine ;
81+ public int errorColumn ;
82+ public String errorType ;
83+ public String errorText ;
84+ public String errorTrace ;
85+ public String msg ;
86+
87+ public ScriptError () {
88+ clear ();
89+ }
90+
91+ public void clear () {
92+ errorLine = -1 ;
93+ errorColumn = -1 ;
94+ errorType = "--UnKnown--" ;
95+ errorText = "--UnKnown--" ;
96+ errorTrace = "" ;
97+ msg = "" ;
98+ }
99+ }
100+
101+ private ScriptError m_script_error ;
102+
75103 protected RubyScriptNodeModel (int inNumInputs , int inNumOutputs , boolean snippetMode ) {
76104 super (inNumInputs , inNumOutputs );
77105
78106 this .numInputs = inNumInputs ;
79107 this .numOutputs = inNumOutputs ;
80108 this .snippetMode = snippetMode ;
109+
110+ this .m_script_error = new ScriptError ();
81111
82112 // define the common imports string
83113 StringBuffer buffer = new StringBuffer ();
84114 buffer .append ("require PLUGIN_PATH+'/rb/knime.rb'\n " );
85- scriptFirstLineNumber = 2 ;
115+ scriptFirstLineNumber = 1 ;
86116
87117 if (this .snippetMode == true ) {
88118 buffer .append ("func = ->(row) do \n " );
@@ -270,11 +300,30 @@ protected final BufferedDataTable[] execute(final BufferedDataTable[] inData,
270300 container .put ("$outColumnTypes" , columnTypes );
271301 container .put ("$exec" , exec );
272302 container .put ("PLUGIN_PATH" , rubyPluginPath );
273-
274- EvalUnit unit = container .parse (scriptHeader + script + scriptFooter ,
275- scriptFirstLineNumber );
276- unit .run ();
277-
303+ String script_fn = "node_script.rb" ;
304+
305+ try {
306+ m_script_error .clear ();
307+ container .setScriptFilename (script_fn );
308+ EvalUnit unit = container .parse (scriptHeader + script
309+ + scriptFooter ,
310+ -scriptFirstLineNumber // fix first string number
311+ );
312+ unit .run ();
313+ } catch (Exception e ) {
314+ Pattern p = Pattern .compile ("SystemExit: ([0-9]+)" );
315+ Matcher matcher = p .matcher (e .toString ());
316+ if (matcher .find ()) {
317+ int exitCode = Integer .parseInt (matcher .group (1 ));
318+ logger .debug ("Exit code: " + exitCode );
319+ } else {
320+ findErrorSource (e , script_fn );
321+ logger .error ("Script error in line: "
322+ + m_script_error .errorLine );
323+ }
324+ throw new CanceledExecutionException (e .getMessage ());
325+ }
326+
278327 BufferedDataTable [] result = new BufferedDataTable [numOutputs ];
279328 for (i = 0 ; i < numOutputs ; i ++) {
280329 outContainer [i ].close ();
@@ -289,6 +338,8 @@ protected final BufferedDataTable[] execute(final BufferedDataTable[] inData,
289338 */
290339 protected final DataTableSpec [] configure (final DataTableSpec [] inSpecs )
291340 throws InvalidSettingsException {
341+ m_script_error .clear ();
342+
292343 appendCols &= numInputs > 0 ;
293344 // append the property columns to the data table spec
294345 DataTableSpec newSpec = appendCols ? inSpecs [0 ] : new DataTableSpec ();
@@ -344,7 +395,7 @@ protected final DataTableSpec[] configure(final DataTableSpec[] inSpecs)
344395 DataTableSpec [] result = new DataTableSpec [numOutputs ];
345396 for (int i = 0 ; i < numOutputs ; i ++) {
346397 result [i ] = newSpec ;
347- }
398+ }
348399 return result ;
349400 }
350401
@@ -420,5 +471,111 @@ public static void setJavaClasspathExtensionPath(String path) {
420471
421472 public static String getJavaClasspathExtensionPath () {
422473 return javaClasspathExtensionsPath ;
423- }
474+ }
475+
476+ private int findErrorSource (Throwable thr , String filename ) {
477+ String err = thr .getMessage ();
478+
479+ if (err .startsWith ("(SyntaxError)" )) {
480+ // org.jruby.parser.ParserSyntaxException
481+ // (SyntaxError) script.rb:2: syntax error, unexpected tRCURLY
482+
483+ Pattern pLineS = Pattern .compile ("(?<=:)(\\ d+):(.*)" );
484+ Matcher mLine = pLineS .matcher (err );
485+ if (mLine .find ()) {
486+ logger .debug ("SyntaxError error line: " + mLine .group (1 ));
487+ m_script_error .errorText = mLine .group (2 ) == null ? m_script_error .errorText
488+ : mLine .group (2 );
489+ logger .debug ("SyntaxError: " + m_script_error .errorText );
490+ m_script_error .errorLine = Integer .parseInt (mLine .group (1 ));
491+ m_script_error .errorColumn = -1 ;
492+ m_script_error .errorType = "SyntaxError" ;
493+ }
494+ } else {
495+ // if (err.startsWith("(NameError)")) {
496+ // org.jruby.embed.EvalFailedException
497+ // (NameError) undefined local variable or method `asdf' for
498+ // main:Object
499+
500+ Pattern type = Pattern .compile ("(?<=\\ ()(\\ w*)" );
501+ Matcher mLine = type .matcher (err );
502+ if (mLine .find ()) {
503+ m_script_error .errorType = mLine .group (1 );
504+ }
505+ Throwable cause = thr .getCause ();
506+ // cause.printStackTrace();
507+ for (StackTraceElement line : cause .getStackTrace ()) {
508+ if (line .getFileName ().equals (filename )) {
509+ m_script_error .errorText = cause .getMessage ();
510+ m_script_error .errorColumn = -1 ;
511+ m_script_error .errorLine = line .getLineNumber ();
512+ m_script_error .errorText = thr .getMessage ();
513+
514+ Pattern knimeType = Pattern
515+ .compile ("(?<=org.knime.)(.*)(?=:)" );
516+ Matcher mKnimeType = knimeType
517+ .matcher (m_script_error .errorText );
518+
519+ if (mKnimeType .find ()) {
520+ m_script_error .errorType = mKnimeType .group (1 );
521+ }
522+
523+ m_script_error .errorType = "RuntimeError" ;
524+
525+ break ;
526+ }
527+ }
528+ }
529+
530+ m_script_error .msg = "script" ;
531+ if (m_script_error .errorLine != -1 ) {
532+ m_script_error .msg += " stopped with error in line "
533+ + m_script_error .errorLine ;
534+ if (m_script_error .errorColumn != -1 ) {
535+ m_script_error .msg += " at column "
536+ + m_script_error .errorColumn ;
537+ }
538+ } else {
539+ m_script_error .msg += "] stopped with error at line --unknown--" ;
540+ }
541+
542+ if (m_script_error .errorType == "RuntimeError" ) {
543+ logger .error (m_script_error .msg + "\n " + m_script_error .errorType
544+ + " ( " + m_script_error .errorText + " )" );
545+
546+ Throwable cause = thr .getCause ();
547+ // cause.printStackTrace();
548+ StackTraceElement [] stack = cause .getStackTrace ();
549+ /*
550+ * StringWriter writer = new StringWriter(); PrintWriter out = new
551+ * PrintWriter(writer); cause.printStackTrace(out); errorTrace =
552+ * writer.toString();
553+ */
554+ StringBuilder builder = new StringBuilder ();
555+ for (StackTraceElement line : stack ) {
556+ builder .append (line .getLineNumber ());
557+ builder .append (":\t " );
558+ builder .append (line .getClassName ());
559+ builder .append (" ( " );
560+ builder .append (line .getMethodName ());
561+ builder .append (" )\t " );
562+ builder .append (line .getFileName ());
563+ builder .append ('\n' );
564+ }
565+
566+ m_script_error .errorTrace = builder .toString ();
567+ if (m_script_error .errorTrace .length () > 0 ) {
568+ logger .error ("--- Traceback --- error source first\n "
569+ + "line: class ( method ) file \n "
570+ + m_script_error .errorTrace
571+ + "[error] --- Traceback --- end --------------" );
572+ }
573+
574+ } else if (m_script_error .errorType != "SyntaxError" ) {
575+ logger .error (m_script_error .msg );
576+ logger .error ("Could not evaluate error source nor reason. Analyze StackTrace!" );
577+ logger .error (err );
578+ }
579+ return m_script_error .errorLine ;
580+ }
424581}
0 commit comments