1414
1515import java .io .File ;
1616import java .io .IOException ;
17+ import java .io .Writer ;
1718import java .net .URI ;
1819import java .nio .file .Files ;
1920import java .nio .file .Path ;
2627import java .util .UUID ;
2728import java .util .stream .Stream ;
2829
30+ import javax .script .ScriptContext ;
2931import javax .script .ScriptEngine ;
3032import javax .script .ScriptException ;
3133
@@ -499,10 +501,23 @@ private void printLoadingMessage(Console console, boolean show) {
499501 }
500502 }
501503
504+ /*
505+ * Configure the engine to redirect output to the provided console.
506+ */
507+ private void configureEngineConsoleOutput (ScriptEngine engine , @ Nullable Console console ) {
508+ if (console != null ) {
509+ ScriptContext context = engine .getContext ();
510+ Writer errorWriter = context .getErrorWriter ();
511+ if (errorWriter != null ) {
512+ context .setErrorWriter (new ConsoleWriter (errorWriter ));
513+ }
514+ }
515+ }
516+
502517 /*
503518 * Create a full openHAB-managed JRuby engine with openHAB scoped variables
504519 * including any injected required gems.
505- *
520+ *
506521 * This will run the script with the helper library if configured.
507522 */
508523 private @ Nullable Object executeWithFullJRuby (Console console , EngineEvalFunction process ) {
@@ -516,6 +531,7 @@ private void printLoadingMessage(Console console, boolean show) {
516531 }
517532 ScriptEngine engine = container .getScriptEngine ();
518533 try {
534+ configureEngineConsoleOutput (engine , console );
519535 printLoadingMessage (console , false );
520536 return process .apply (engine );
521537 } catch (ScriptException e ) {
@@ -535,6 +551,7 @@ private void printLoadingMessage(Console console, boolean show) {
535551 if (engine == null ) {
536552 throw new ScriptException ("Unable to create JRuby script engine." );
537553 }
554+ configureEngineConsoleOutput (engine , console );
538555 return process .apply (engine );
539556 } catch (ScriptException e ) {
540557 if (console != null ) {
@@ -546,6 +563,70 @@ private void printLoadingMessage(Console console, boolean show) {
546563 }
547564 }
548565
566+ // ============================================================================
567+ // Inner Classes
568+ // ============================================================================
569+
570+ /**
571+ * A Writer wrapper that normalizes LF line endings to CRLF while preserving all other characters.
572+ */
573+ static class ConsoleWriter extends Writer {
574+ private final Writer delegate ;
575+ private boolean previousWasCarriageReturn = false ;
576+ private boolean closed = false ;
577+
578+ ConsoleWriter (Writer delegate ) {
579+ this .delegate = delegate ;
580+ }
581+
582+ @ Override
583+ public void write (char @ Nullable [] c , int off , int len ) throws IOException {
584+ if (closed ) {
585+ throw new IOException ("Writer is closed" );
586+ }
587+ if (c != null ) {
588+ for (int index = off ; index < off + len ; index ++) {
589+ char ch = c [index ];
590+ if (previousWasCarriageReturn ) {
591+ if (ch == '\n' ) {
592+ delegate .write ('\r' );
593+ delegate .write ('\n' );
594+ previousWasCarriageReturn = false ;
595+ continue ;
596+ }
597+ delegate .write ('\r' );
598+ previousWasCarriageReturn = false ;
599+ }
600+
601+ if (ch == '\r' ) {
602+ previousWasCarriageReturn = true ;
603+ } else if (ch == '\n' ) {
604+ delegate .write ('\r' );
605+ delegate .write ('\n' );
606+ } else {
607+ delegate .write (ch );
608+ }
609+ }
610+ }
611+ }
612+
613+ @ Override
614+ public void flush () throws IOException {
615+ if (previousWasCarriageReturn ) {
616+ delegate .write ('\r' );
617+ previousWasCarriageReturn = false ;
618+ }
619+ delegate .flush ();
620+ }
621+
622+ @ Override
623+ public void close () throws IOException {
624+ flush ();
625+ closed = true ;
626+ delegate .close ();
627+ }
628+ }
629+
549630 @ FunctionalInterface
550631 public interface EngineEvalFunction {
551632 @ Nullable
0 commit comments