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,21 @@ 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+ context .setWriter (new ConsoleWriter (console ));
511+ context .setErrorWriter (new ConsoleWriter (console ));
512+ }
513+ }
514+
502515 /*
503516 * Create a full openHAB-managed JRuby engine with openHAB scoped variables
504517 * including any injected required gems.
505- *
518+ *
506519 * This will run the script with the helper library if configured.
507520 */
508521 private @ Nullable Object executeWithFullJRuby (Console console , EngineEvalFunction process ) {
@@ -516,6 +529,7 @@ private void printLoadingMessage(Console console, boolean show) {
516529 }
517530 ScriptEngine engine = container .getScriptEngine ();
518531 try {
532+ configureEngineConsoleOutput (engine , console );
519533 printLoadingMessage (console , false );
520534 return process .apply (engine );
521535 } catch (ScriptException e ) {
@@ -535,6 +549,7 @@ private void printLoadingMessage(Console console, boolean show) {
535549 if (engine == null ) {
536550 throw new ScriptException ("Unable to create JRuby script engine." );
537551 }
552+ configureEngineConsoleOutput (engine , console );
538553 return process .apply (engine );
539554 } catch (ScriptException e ) {
540555 if (console != null ) {
@@ -546,6 +561,65 @@ private void printLoadingMessage(Console console, boolean show) {
546561 }
547562 }
548563
564+ // ============================================================================
565+ // Inner Classes
566+ // ============================================================================
567+
568+ /**
569+ * A custom Writer implementation that routes output through the console.
570+ * Properly handles newlines by converting them to console.println calls.
571+ */
572+ private static class ConsoleWriter extends Writer {
573+ private final Console console ;
574+ private final StringBuilder buffer = new StringBuilder ();
575+ private boolean closed = false ;
576+
577+ ConsoleWriter (Console console ) {
578+ this .console = console ;
579+ }
580+
581+ @ Override
582+ public void write (char @ Nullable [] c , int off , int len ) throws IOException {
583+ if (closed ) {
584+ throw new IOException ("Writer is closed" );
585+ }
586+ if (c != null ) {
587+ buffer .append (c , off , len );
588+ flushBuffer ();
589+ }
590+ }
591+
592+ private void flushBuffer () {
593+ String content = buffer .toString ();
594+ int lastNewlineIndex = content .lastIndexOf ('\n' );
595+
596+ if (lastNewlineIndex >= 0 ) {
597+ // Process all complete lines
598+ String [] lines = content .substring (0 , lastNewlineIndex + 1 ).split ("\n " , -1 );
599+ for (int i = 0 ; i < lines .length - 1 ; i ++) {
600+ console .println (lines [i ]);
601+ }
602+ // Keep any remaining partial line in the buffer
603+ buffer .delete (0 , lastNewlineIndex + 1 );
604+ }
605+ }
606+
607+ @ Override
608+ public void flush () throws IOException {
609+ // Flush any remaining content
610+ if (buffer .length () > 0 ) {
611+ console .print (buffer .toString ());
612+ buffer .delete (0 , buffer .length ());
613+ }
614+ }
615+
616+ @ Override
617+ public void close () throws IOException {
618+ flush ();
619+ closed = true ;
620+ }
621+ }
622+
549623 @ FunctionalInterface
550624 public interface EngineEvalFunction {
551625 @ Nullable
0 commit comments