You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2016/06/14 13:23:17 UTC

[2/6] karaf git commit: [KARAF-4570] Upgrade to JLine 3

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/core/src/main/java/org/apache/karaf/shell/impl/console/standalone/Main.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/standalone/Main.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/standalone/Main.java
index dd9ed00..03290e0 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/standalone/Main.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/standalone/Main.java
@@ -44,10 +44,9 @@ import org.apache.karaf.shell.api.console.Terminal;
 import org.apache.karaf.shell.impl.action.command.ManagerImpl;
 import org.apache.karaf.shell.impl.console.JLineTerminal;
 import org.apache.karaf.shell.impl.console.SessionFactoryImpl;
-import org.apache.karaf.shell.impl.console.TerminalFactory;
 import org.apache.karaf.shell.support.NameScoping;
 import org.apache.karaf.shell.support.ShellUtil;
-import org.fusesource.jansi.AnsiConsole;
+import org.jline.terminal.TerminalBuilder;
 
 public class Main {
 
@@ -67,12 +66,13 @@ public class Main {
      */
     public void run(String args[]) throws Exception {
 
+        InputStream in = System.in;
+        PrintStream out = System.out;
+        PrintStream err = System.err;
+
         ThreadIOImpl threadio = new ThreadIOImpl();
         threadio.start();
 
-        InputStream in = unwrap(System.in);
-        PrintStream out = wrap(unwrap(System.out));
-        PrintStream err = wrap(unwrap(System.err));
         run(threadio, args, in, out, err);
 
         // TODO: do we need to stop the threadio that was started?
@@ -138,10 +138,8 @@ public class Main {
 
     private void run(final SessionFactory sessionFactory, String command, final InputStream in, final PrintStream out, final PrintStream err, ClassLoader cl) throws Exception {
 
-        final TerminalFactory terminalFactory = new TerminalFactory();
-        try {
-            String term = System.getenv("TERM");
-            final Terminal terminal = new JLineTerminal(terminalFactory.getTerminal(), term);
+        try (org.jline.terminal.Terminal jlineTerminal = TerminalBuilder.terminal()) {
+            final Terminal terminal = new JLineTerminal(jlineTerminal);
             Session session = createSession(sessionFactory, command.length() > 0 ? null : in, out, err, terminal);
             session.put("USER", user);
             session.put("APPLICATION", application);
@@ -163,8 +161,6 @@ public class Main {
                 // We are going into full blown interactive shell mode.
                 session.run();
             }
-        } finally {
-            terminalFactory.destroy();
         }
     }
 
@@ -246,24 +242,6 @@ public class Main {
         return true;
     }
 
-    private static PrintStream wrap(PrintStream stream) {
-        OutputStream o = AnsiConsole.wrapOutputStream(stream);
-        if (o instanceof PrintStream) {
-            return ((PrintStream) o);
-        } else {
-            return new PrintStream(o);
-        }
-    }
-
-    private static <T> T unwrap(T stream) {
-        try {
-            Method mth = stream.getClass().getMethod("getRoot");
-            return (T) mth.invoke(stream);
-        } catch (Throwable t) {
-            return stream;
-        }
-    }
-
     private static List<URL> getFiles(File base) throws MalformedURLException {
         List<URL> urls = new ArrayList<URL>();
         getFiles(base, urls);

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/core/src/main/java/org/apache/karaf/shell/support/ansi/AnsiSplitter.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/support/ansi/AnsiSplitter.java b/shell/core/src/main/java/org/apache/karaf/shell/support/ansi/AnsiSplitter.java
index d729cfc..b54ecf1 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/support/ansi/AnsiSplitter.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/support/ansi/AnsiSplitter.java
@@ -18,39 +18,42 @@
  */
 package org.apache.karaf.shell.support.ansi;
 
-import java.io.ByteArrayOutputStream;
+import java.io.BufferedReader;
 import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
-import org.fusesource.jansi.Ansi;
-import org.fusesource.jansi.AnsiOutputStream;
+import org.jline.utils.AttributedString;
+import org.jline.utils.AttributedStringBuilder;
 
 public class AnsiSplitter {
 
     public static List<String> splitLines(String text, int maxLength, int tabs) throws IOException {
-        AnsiOutputStreamSplitter splitter = new AnsiOutputStreamSplitter(maxLength);
-        splitter.setTabs(tabs);
-        splitter.write(text.getBytes());
-        splitter.close();
-        return splitter.lines;
+        AttributedStringBuilder sb = new AttributedStringBuilder();
+        sb.tabs(tabs);
+        sb.appendAnsi(text);
+        return sb.columnSplitLength(maxLength)
+                .stream()
+                .map(AttributedString::toAnsi)
+                .collect(Collectors.toList());
     }
 
     public static String substring(String text, int begin, int end, int tabs) throws IOException {
-        AnsiOutputStreamSplitter splitter = new AnsiOutputStreamSplitter(begin, end, Integer.MAX_VALUE);
-        splitter.setTabs(tabs);
-        splitter.write(text.getBytes());
-        splitter.close();
-        return splitter.lines.get(0);
+        AttributedStringBuilder sb = new AttributedStringBuilder();
+        sb.tabs(tabs);
+        sb.appendAnsi(text);
+        return sb.columnSubSequence(begin, end).toAnsi();
     }
 
-    public static int length(String curLine, int tabs) throws IOException {
-        AnsiOutputStreamSplitter splitter = new AnsiOutputStreamSplitter(0, Integer.MAX_VALUE, Integer.MAX_VALUE);
-        splitter.setTabs(tabs);
-        splitter.write(curLine.getBytes());
-        return splitter.getRealLength();
+    public static int length(String text, int tabs) throws IOException {
+        AttributedStringBuilder sb = new AttributedStringBuilder();
+        sb.tabs(tabs);
+        sb.appendAnsi(text);
+        return sb.columnLength();
     }
 
     public static String cut(String text, int maxLength, int tabs)  throws IOException {
@@ -72,29 +75,44 @@ public class AnsiSplitter {
 
     public static class AnsiBufferedReader implements Closeable {
 
-        private final InputStream in;
-        private final AnsiOutputStreamSplitter splitter;
+        private final BufferedReader reader;
+        private final int begin;
+        private final int end;
+        private final int maxLength;
+        private final AttributedStringBuilder builder;
+        private final List<String> lines;
 
         public AnsiBufferedReader(InputStream in, int begin, int end, int maxLength) {
-            this.in = in;
-            this.splitter = new AnsiOutputStreamSplitter(begin, end, maxLength);
+            this.reader = new BufferedReader(new InputStreamReader(in));
+            this.begin = begin;
+            this.end = end;
+            this.maxLength = maxLength;
+            this.builder = new AttributedStringBuilder();
+            this.lines = new ArrayList<>();
         }
 
         public String readLine() throws IOException {
-            while (splitter.lines.isEmpty()) {
-                int c = in.read();
-                if (c < 0) {
-                    splitter.flushLine(false);
-                    break;
+            if (lines.isEmpty()) {
+                String line = reader.readLine();
+                if (line == null) {
+                    return null;
+                }
+                if (line.isEmpty()) {
+                    lines.add("");
                 } else {
-                    splitter.write(c);
+                    builder.setLength(0);
+                    builder.appendAnsi(line);
+                    if (builder.length() > 0) {
+                        builder.style(builder.styleAt(builder.length() - 1));
+                    }
+                    AttributedString str = builder.columnSubSequence(begin, end);
+                    str.columnSplitLength(maxLength)
+                            .stream()
+                            .map(AttributedString::toAnsi)
+                            .forEach(lines::add);
                 }
             }
-            if (splitter.lines.isEmpty()) {
-                return null;
-            } else {
-                return splitter.lines.remove(0);
-            }
+            return lines.remove(0);
         }
 
         @Override
@@ -102,304 +120,8 @@ public class AnsiSplitter {
         }
 
         public void setTabs(int tabs) {
-            this.splitter.setTabs(tabs);
+            this.builder.tabs(tabs);
         }
     }
 
-    static class AnsiOutputStreamSplitter extends AnsiOutputStream {
-
-        protected static final int ATTRIBUTE_NEGATIVE_OFF = 27;
-
-        Ansi.Attribute intensity;
-        Ansi.Attribute underline;
-        Ansi.Attribute blink;
-        Ansi.Attribute negative;
-        Ansi.Color fg;
-        Ansi.Color bg;
-
-        private int begin;
-        private int length;
-        private int maxLength;
-        private int escapeLength;
-        private int windowState;
-        private int tabs;
-        private List<String> lines = new ArrayList<>();
-
-        public AnsiOutputStreamSplitter(int maxLength) {
-            this(0, Integer.MAX_VALUE, maxLength);
-        }
-
-        public AnsiOutputStreamSplitter(int begin, int end, int maxLength) {
-            super(new ByteArrayOutputStream());
-            this.begin = begin;
-            this.length = end - begin;
-            this.maxLength = maxLength - begin;
-            this.windowState = begin > 0 ? 0 : 1;
-            reset();
-        }
-
-        public int getTabs() {
-            return tabs;
-        }
-
-        public void setTabs(int tabs) {
-            this.tabs = tabs;
-        }
-
-        protected void reset() {
-            intensity = Ansi.Attribute.INTENSITY_BOLD_OFF;
-            underline = Ansi.Attribute.UNDERLINE_OFF;
-            blink = Ansi.Attribute.BLINK_OFF;
-            negative = Ansi.Attribute.NEGATIVE_OFF;
-            fg = Ansi.Color.DEFAULT;
-            bg = Ansi.Color.DEFAULT;
-        }
-
-        public int getRealLength() {
-            return ((ByteArrayOutputStream) out).size() - escapeLength;
-        }
-
-        @Override
-        public void write(int data) throws IOException {
-            if (data == '\n') {
-                flushLine(true);
-            } else if (data == '\t') {
-                ByteArrayOutputStream baos = (ByteArrayOutputStream) out;
-                do {
-                    write(' ');
-                } while ((baos.size() - escapeLength) % tabs > 0);
-            } else {
-                if (windowState != 2) {
-                    super.write(data);
-                }
-                ByteArrayOutputStream baos = (ByteArrayOutputStream) out;
-                if (windowState == 0 && baos.size() - escapeLength > begin) {
-                    windowState = 1;
-                    int nbMissing = baos.size() - escapeLength - begin;
-                    byte[] old = baos.toByteArray();
-                    beginAttributes();
-                    baos.write(old, old.length - nbMissing, nbMissing);
-                } else if (windowState == 1 && baos.size() - escapeLength >= length) {
-                    windowState = 2;
-                    endAttributes();
-                    reset();
-                }
-                if (baos.size() - escapeLength >= maxLength) {
-                    flushLine(true);
-                }
-            }
-        }
-
-        @Override
-        public void close() throws IOException {
-            if (windowState == 0) {
-                beginAttributes();
-            }
-            flushLine(lines.isEmpty());
-            super.close();
-        }
-
-        private void flushLine(boolean force) throws IOException {
-            ByteArrayOutputStream baos = (ByteArrayOutputStream) out;
-            if (windowState == 0) {
-                beginAttributes();
-            }
-            if (force || baos.size() > escapeLength) {
-                endAttributes();
-                lines.add(new String(baos.toByteArray()));
-                beginAttributes();
-            }
-            windowState = 0;
-        }
-
-        private void endAttributes() throws IOException {
-            if (intensity != Ansi.Attribute.INTENSITY_BOLD_OFF) {
-                setAttribute(Ansi.Attribute.INTENSITY_BOLD_OFF);
-            }
-            if (underline != Ansi.Attribute.UNDERLINE_OFF) {
-                setAttribute(Ansi.Attribute.UNDERLINE_OFF);
-            }
-            if (blink != Ansi.Attribute.BLINK_OFF) {
-                setAttribute(Ansi.Attribute.BLINK_OFF);
-            }
-            if (negative != Ansi.Attribute.NEGATIVE_OFF) {
-                setAttribute(Ansi.Attribute.NEGATIVE_OFF);
-            }
-            if (fg != Ansi.Color.DEFAULT) {
-                setAttributeFg(Ansi.Color.DEFAULT);
-            }
-            if (bg != Ansi.Color.DEFAULT) {
-                setAttributeBg(Ansi.Color.DEFAULT);
-            }
-        }
-
-        private void beginAttributes() throws IOException {
-            ((ByteArrayOutputStream) out).reset();
-            escapeLength = 0;
-            if (intensity != Ansi.Attribute.INTENSITY_BOLD_OFF) {
-                setAttribute(intensity);
-            }
-            if (underline != Ansi.Attribute.UNDERLINE_OFF) {
-                setAttribute(underline);
-            }
-            if (blink != Ansi.Attribute.BLINK_OFF) {
-                setAttribute(blink);
-            }
-            if (negative != Ansi.Attribute.NEGATIVE_OFF) {
-                setAttribute(negative);
-            }
-            if (fg != Ansi.Color.DEFAULT) {
-                setAttributeFg(fg);
-            }
-            if (bg != Ansi.Color.DEFAULT) {
-                setAttributeBg(bg);
-            }
-        }
-
-        @Override
-        protected void processAttributeRest() throws IOException {
-            setAttribute(Ansi.Attribute.RESET);
-            reset();
-        }
-
-        @Override
-        protected void processSetAttribute(int attribute) throws IOException {
-            switch(attribute) {
-            case ATTRIBUTE_INTENSITY_BOLD:
-                setIntensity(Ansi.Attribute.INTENSITY_BOLD);
-                break;
-            case ATTRIBUTE_INTENSITY_FAINT:
-                setIntensity(Ansi.Attribute.INTENSITY_FAINT);
-                break;
-            case ATTRIBUTE_INTENSITY_NORMAL:
-                setIntensity(Ansi.Attribute.INTENSITY_BOLD_OFF);
-                break;
-            case ATTRIBUTE_UNDERLINE:
-                setUnderline(Ansi.Attribute.UNDERLINE);
-                break;
-            case ATTRIBUTE_UNDERLINE_DOUBLE:
-                setUnderline(Ansi.Attribute.UNDERLINE_DOUBLE);
-                break;
-            case ATTRIBUTE_UNDERLINE_OFF:
-                setUnderline(Ansi.Attribute.UNDERLINE_OFF);
-                break;
-            case ATTRIBUTE_BLINK_OFF:
-                setBlink(Ansi.Attribute.BLINK_OFF);
-                break;
-            case ATTRIBUTE_BLINK_SLOW:
-                setBlink(Ansi.Attribute.BLINK_SLOW);
-                break;
-            case ATTRIBUTE_BLINK_FAST:
-                setBlink(Ansi.Attribute.BLINK_FAST);
-                break;
-            case ATTRIBUTE_NEGATIVE_ON:
-                setNegative(Ansi.Attribute.NEGATIVE_ON);
-                break;
-            case ATTRIBUTE_NEGATIVE_OFF:
-                setNegative(Ansi.Attribute.NEGATIVE_OFF);
-                break;
-            default:
-                break;
-            }
-        }
-
-        @Override
-        protected void processSetForegroundColor(int color) throws IOException {
-            Ansi.Color c;
-            switch (color) {
-            case 0: c = Ansi.Color.BLACK; break;
-            case 1: c = Ansi.Color.RED; break;
-            case 2: c = Ansi.Color.GREEN; break;
-            case 3: c = Ansi.Color.YELLOW; break;
-            case 4: c = Ansi.Color.BLUE; break;
-            case 5: c = Ansi.Color.MAGENTA; break;
-            case 6: c = Ansi.Color.CYAN; break;
-            case 7: c = Ansi.Color.WHITE; break;
-            case 9: c = Ansi.Color.DEFAULT; break;
-            default: return;
-            }
-            if (this.fg != c) {
-                this.fg = c;
-                setAttributeFg(c);
-            }
-        }
-
-        @Override
-        protected void processSetBackgroundColor(int color) throws IOException {
-            Ansi.Color c;
-            switch (color) {
-            case 0: c = Ansi.Color.BLACK; break;
-            case 1: c = Ansi.Color.RED; break;
-            case 2: c = Ansi.Color.GREEN; break;
-            case 3: c = Ansi.Color.YELLOW; break;
-            case 4: c = Ansi.Color.BLUE; break;
-            case 5: c = Ansi.Color.MAGENTA; break;
-            case 6: c = Ansi.Color.CYAN; break;
-            case 7: c = Ansi.Color.WHITE; break;
-            case 9: c = Ansi.Color.DEFAULT; break;
-            default: return;
-            }
-            if (this.bg != c) {
-                this.bg = c;
-                setAttributeBg(c);
-            }
-        }
-
-        @Override
-        protected void processDefaultTextColor() throws IOException {
-            processSetForegroundColor(9);
-        }
-
-        @Override
-        protected void processDefaultBackgroundColor() throws IOException {
-            processSetBackgroundColor(9);
-        }
-
-        protected void setIntensity(Ansi.Attribute intensity) throws IOException {
-            if (this.intensity != intensity) {
-                this.intensity = intensity;
-                setAttribute(intensity);
-            }
-        }
-
-        protected void setUnderline(Ansi.Attribute underline) throws IOException {
-            if (this.underline != underline) {
-                this.underline = underline;
-                setAttribute(underline);
-            }
-        }
-
-        protected void setBlink(Ansi.Attribute blink) throws IOException {
-            if (this.blink != blink) {
-                this.blink = blink;
-                setAttribute(blink);
-            }
-        }
-
-        protected void setNegative(Ansi.Attribute negative) throws IOException {
-            if (this.negative != negative) {
-                this.negative = negative;
-                setAttribute(negative);
-            }
-        }
-
-        private void setAttributeFg(Ansi.Color color) throws IOException {
-            String sequence = Ansi.ansi().fg(color).toString();
-            escapeLength += sequence.length();
-            out.write(sequence.getBytes());
-        }
-
-        private void setAttributeBg(Ansi.Color color) throws IOException {
-            String sequence = Ansi.ansi().bg(color).toString();
-            escapeLength += sequence.length();
-            out.write(sequence.getBytes());
-        }
-
-        private void setAttribute(Ansi.Attribute attribute) throws IOException {
-            String sequence = Ansi.ansi().a(attribute).toString();
-            escapeLength += sequence.length();
-            out.write(sequence.getBytes());
-        }
-
-    }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/core/src/main/java/org/apache/karaf/shell/support/ansi/SimpleAnsi.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/support/ansi/SimpleAnsi.java b/shell/core/src/main/java/org/apache/karaf/shell/support/ansi/SimpleAnsi.java
index 92ce8b6..a0b2566 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/support/ansi/SimpleAnsi.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/support/ansi/SimpleAnsi.java
@@ -18,14 +18,14 @@
  */
 package org.apache.karaf.shell.support.ansi;
 
-import org.fusesource.jansi.Ansi;
-import org.fusesource.jansi.Ansi.Color;
-
 public class SimpleAnsi {
-    public static String COLOR_RED = Ansi.ansi().fg(Color.RED).toString();
-    public static String COLOR_CYAN = Ansi.ansi().fg(Color.CYAN).toString();
-    public static String COLOR_DEFAULT = Ansi.ansi().fg(Color.DEFAULT).toString();
+    public static String COLOR_RED = "\u001b[31m";
+    public static String COLOR_CYAN = "\u001b[36m";
+    public static String COLOR_YELLOW = "\u001b[33m";
+    public static String COLOR_DEFAULT = "\u001b[39m";
     
-    public static String INTENSITY_BOLD = Ansi.ansi().bold().toString();
-    public static String INTENSITY_NORMAL = Ansi.ansi().boldOff().toString();
+    public static String INTENSITY_BOLD = "\u001b[1m";
+    public static String INTENSITY_NORMAL = "\u001b[22m";
+
+    public static String RESET = "\u001b[m";
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/core/src/main/java/org/apache/karaf/shell/support/completers/AggregateCompleter.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/support/completers/AggregateCompleter.java b/shell/core/src/main/java/org/apache/karaf/shell/support/completers/AggregateCompleter.java
index 580f4d2..3a0b58c 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/support/completers/AggregateCompleter.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/support/completers/AggregateCompleter.java
@@ -21,9 +21,9 @@ package org.apache.karaf.shell.support.completers;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.LinkedList;
 import java.util.List;
 
+import org.apache.karaf.shell.api.console.Candidate;
 import org.apache.karaf.shell.api.console.CommandLine;
 import org.apache.karaf.shell.api.console.Completer;
 import org.apache.karaf.shell.api.console.Session;
@@ -42,55 +42,22 @@ public class AggregateCompleter implements Completer
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
-    public int complete(final Session session, final CommandLine commandLine, final List candidates) {
+    public int complete(final Session session, final CommandLine commandLine, final List<String> candidates) {
+        List<Candidate> cands = new ArrayList<>();
+        completeCandidates(session, commandLine, cands);
+        for (Candidate cand : cands) {
+            candidates.add(cand.value());
+        }
+        return 0;
+    }
+
+    @Override
+    public void completeCandidates(Session session, CommandLine commandLine, List<Candidate> candidates) {
         // buffer could be null
         assert candidates != null;
-
-        List<Completion> completions = new ArrayList<Completion>(completers.size());
-
-        // Run each completer, saving its completion results
-        int max = -1;
         for (Completer completer : completers) {
-            Completion completion = new Completion(candidates);
-            completion.complete(session, completer, commandLine);
-
-            // Compute the max cursor position
-            if (completion.cursor > max) {
-                completions.clear();
-                completions.add(completion);
-                max = completion.cursor;
-            } else if (completion.cursor == max) {
-                completions.add(completion);
-            }
-        }
-
-        // Append candidates from completions which have the same cursor position as max
-        for (Completion completion : completions) {
-            // noinspection unchecked
-            candidates.addAll(completion.candidates);
+            completer.completeCandidates(session, commandLine, candidates);
         }
-
-        return max;
     }
 
-    private class Completion
-    {
-        public final List<String> candidates;
-
-        public int cursor;
-
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        public Completion(final List candidates) {
-            assert candidates != null;
-
-            // noinspection unchecked
-            this.candidates = new LinkedList<String>(candidates);
-        }
-
-        public void complete(final Session session, final Completer completer, final CommandLine commandLine) {
-            assert completer != null;
-
-            this.cursor = completer.complete(session, commandLine, candidates);
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/core/src/main/java/org/apache/karaf/shell/support/completers/FileCompleter.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/support/completers/FileCompleter.java b/shell/core/src/main/java/org/apache/karaf/shell/support/completers/FileCompleter.java
index 4a96d52..6bf8f4d 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/support/completers/FileCompleter.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/support/completers/FileCompleter.java
@@ -53,8 +53,7 @@ public class FileCompleter implements Completer
     private static final boolean OS_IS_WINDOWS = isWindows();
     
     public static boolean isWindows() {
-        return (OS.indexOf("win") >= 0);
-
+        return OS.contains("win");
     }
 
     public int complete(final Session session, CommandLine commandLine, final List<String> candidates) {

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/core/src/main/java/org/apache/karaf/shell/support/table/AnsiColumn.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/support/table/AnsiColumn.java b/shell/core/src/main/java/org/apache/karaf/shell/support/table/AnsiColumn.java
index 4d5b0e7..260c5c7 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/support/table/AnsiColumn.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/support/table/AnsiColumn.java
@@ -15,17 +15,17 @@
  */
 package org.apache.karaf.shell.support.table;
 
-import org.fusesource.jansi.Ansi;
+import org.jline.utils.AttributedStringBuilder;
 
 /**
  * Colored support for column.
  */
 public class AnsiColumn extends Col {
 
-    private Ansi.Color color;
+    private int color;
     private boolean bold;
 
-    public AnsiColumn(String header, Ansi.Color color, boolean bold) {
+    public AnsiColumn(String header, int color, boolean bold) {
         super(header);
         this.color = color;
         this.bold = bold;
@@ -35,20 +35,20 @@ public class AnsiColumn extends Col {
     public String getContent(String content) {
         String in = super.getContent(content);
 
-        Ansi ansi = Ansi.ansi();
-        ansi.fg(color);
+        AttributedStringBuilder sb = new AttributedStringBuilder();
+        sb.style(sb.style().foreground(color));
 
         if (bold)
-            ansi.a(Ansi.Attribute.INTENSITY_BOLD);
+            sb.style(sb.style().bold());
 
-        ansi.a(in);
+        sb.append(in);
 
         if (bold)
-            ansi.a(Ansi.Attribute.INTENSITY_BOLD_OFF);
+            sb.style(sb.style().boldOff());
 
-        ansi.fg(Ansi.Color.DEFAULT);
+        sb.style(sb.style().foregroundOff());
 
-        return ansi.toString();
+        return sb.toAnsi();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/core/src/main/java/org/apache/karaf/shell/support/terminal/SignalSupport.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/support/terminal/SignalSupport.java b/shell/core/src/main/java/org/apache/karaf/shell/support/terminal/SignalSupport.java
index 39ff0f7..b0c2b8c 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/support/terminal/SignalSupport.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/support/terminal/SignalSupport.java
@@ -22,6 +22,7 @@ import java.util.EnumSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.CopyOnWriteArraySet;
 
 import org.apache.karaf.shell.api.console.Signal;
@@ -29,11 +30,7 @@ import org.apache.karaf.shell.api.console.SignalListener;
 
 public class SignalSupport {
 
-    private final Map<Signal, Set<SignalListener>> listeners;
-
-    public SignalSupport() {
-        listeners = new ConcurrentHashMap<>(3);
-    }
+    protected final ConcurrentMap<Signal, Set<SignalListener>> listeners = new ConcurrentHashMap<>(3);
 
     public void addSignalListener(SignalListener listener, Signal... signals) {
         if (signals == null) {
@@ -80,17 +77,6 @@ public class SignalSupport {
     }
 
     protected Set<SignalListener> getSignalListeners(Signal signal, boolean create) {
-        Set<SignalListener> ls = listeners.get(signal);
-        if (ls == null && create) {
-            synchronized (listeners) {
-                ls = listeners.get(signal);
-                if (ls == null) {
-                    ls = new CopyOnWriteArraySet<>();
-                    listeners.put(signal, ls);
-                }
-            }
-        }
-        // may be null in case create=false
-        return ls;
+        return listeners.compute(signal, (sig, lst) -> lst != null ? lst : create ? new CopyOnWriteArraySet<>() : null);
     }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/core/src/test/java/org/apache/karaf/shell/impl/console/CompleterAsCompletorTest.java
----------------------------------------------------------------------
diff --git a/shell/core/src/test/java/org/apache/karaf/shell/impl/console/CompleterAsCompletorTest.java b/shell/core/src/test/java/org/apache/karaf/shell/impl/console/CompleterAsCompletorTest.java
deleted file mode 100644
index abe740a..0000000
--- a/shell/core/src/test/java/org/apache/karaf/shell/impl/console/CompleterAsCompletorTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.karaf.shell.impl.console;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
-import org.apache.karaf.shell.api.console.CommandLine;
-import org.apache.karaf.shell.api.console.Completer;
-import org.apache.karaf.shell.api.console.Session;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class CompleterAsCompletorTest {
-
-    @Test
-    public void testCompletionMultiCommand() {
-        SessionFactoryImpl sessionFactory = new SessionFactoryImpl(new ThreadIOImpl());
-        Session session = new HeadlessSessionImpl(sessionFactory, sessionFactory.getCommandProcessor(),
-                new ByteArrayInputStream(new byte[0]), new PrintStream(new ByteArrayOutputStream()), new PrintStream(new ByteArrayOutputStream())
-        );
-
-        Completer completer = new Completer() {
-            @Override
-            public int complete(Session session, CommandLine commandLine, List<String> candidates) {
-                assertEquals(" bundle:l", commandLine.getBuffer());
-                candidates.add("bundle:list");
-                return 1;
-            }
-        };
-        jline.console.completer.Completer cmp = new CompleterAsCompletor(session, completer);
-
-        String cmd = "bundle:list ; bundle:l";
-        List<CharSequence> candidates = new ArrayList<>();
-        int pos = cmp.complete(cmd, cmd.length(), candidates);
-
-        assertEquals("bundle:list ; ".length(), pos);
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/core/src/test/java/org/apache/karaf/shell/support/ansi/AnsiSplitterTest.java
----------------------------------------------------------------------
diff --git a/shell/core/src/test/java/org/apache/karaf/shell/support/ansi/AnsiSplitterTest.java b/shell/core/src/test/java/org/apache/karaf/shell/support/ansi/AnsiSplitterTest.java
index 3d490bf..c4474ec 100644
--- a/shell/core/src/test/java/org/apache/karaf/shell/support/ansi/AnsiSplitterTest.java
+++ b/shell/core/src/test/java/org/apache/karaf/shell/support/ansi/AnsiSplitterTest.java
@@ -29,7 +29,7 @@ public class AnsiSplitterTest {
     @Test
     public void testWindow() throws IOException {
         String text = "\u001B[1mThis is bold.\u001B[22m";
-        assertEquals("\u001B[1mis\u001B[22m", AnsiSplitter.substring(text, 5, 7, 4));
+        assertEquals("\u001B[1mis\u001B[0m", AnsiSplitter.substring(text, 5, 7, 4));
         assertEquals(13, AnsiSplitter.length(text, 4));
     }
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/pom.xml
----------------------------------------------------------------------
diff --git a/shell/pom.xml b/shell/pom.xml
index 493a42e..f37c320 100644
--- a/shell/pom.xml
+++ b/shell/pom.xml
@@ -37,8 +37,8 @@
         <module>commands</module>
         <module>core</module>
         <module>console</module>
-        <module>ssh</module>
         <module>table</module>
+        <module>ssh</module>
     </modules>
 
 </project>

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ShellFactoryImpl.java
----------------------------------------------------------------------
diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ShellFactoryImpl.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ShellFactoryImpl.java
index 4e8dc58..f7fdf42 100644
--- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ShellFactoryImpl.java
+++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ShellFactoryImpl.java
@@ -19,7 +19,6 @@
 package org.apache.karaf.shell.ssh;
 
 import java.io.Closeable;
-import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -68,6 +67,10 @@ public class ShellFactoryImpl implements Factory<Command> {
 
         private ServerSession session;
 
+        private Session shell;
+
+        private SshTerminal terminal;
+
         private boolean closed;
 
         public void setInputStream(final InputStream in) {
@@ -94,38 +97,27 @@ public class ShellFactoryImpl implements Factory<Command> {
             try {
                 final Subject subject = ShellImpl.this.session != null ? ShellImpl.this.session
                         .getAttribute(KarafJaasAuthenticator.SUBJECT_ATTRIBUTE_KEY) : null;
-                final Terminal terminal = new SshTerminal(env);
-                Runnable destroyCallback = new Runnable() {
-                    public void run() {
-                        destroy();
-                    }
-                };
+                final PrintStream pout = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out);
+                final PrintStream perr = err instanceof PrintStream ? (PrintStream) err : out == err ? pout : new PrintStream(err);
+                terminal = new SshTerminal(env, in, pout);
                 String encoding = getEncoding();
-                final Session session = sessionFactory.create(in,
-                        lfToCrLfPrintStream(out), lfToCrLfPrintStream(err), terminal, encoding, destroyCallback);
+                shell = sessionFactory.create(in,
+                        pout, perr, terminal, encoding, this::destroy);
                 for (Map.Entry<String, String> e : env.getEnv().entrySet()) {
-                    session.put(e.getKey(), e.getValue());
+                    shell.put(e.getKey(), e.getValue());
                 }
-                JaasHelper.doAs(subject, new PrivilegedAction<Object>() {
-                    public Object run() {
-                        new Thread(session, "Karaf ssh console user " + ShellUtil.getCurrentUserName()).start();
-                        return null;
-                    }
-                });
+                JaasHelper.runAs(subject, () ->
+                    new Thread(shell, "Karaf ssh console user " + ShellUtil.getCurrentUserName()).start());
             } catch (Exception e) {
                 throw (IOException) new IOException("Unable to start shell").initCause(e);
             }
         }
 
-        private PrintStream lfToCrLfPrintStream(OutputStream stream) {
-            return new PrintStream(new LfToCrLfFilterOutputStream(stream), true);
-        }
-
         public void destroy() {
             if (!closed) {
                 closed = true;
-                ShellFactoryImpl.flush(out, err);
-                ShellFactoryImpl.close(in, out, err);
+                flush(out, err);
+                close(in, out, err);
                 callback.onExit(0);
             }
         }
@@ -180,32 +172,10 @@ public class ShellFactoryImpl implements Factory<Command> {
         for (Closeable c : closeables) {
             try {
                 c.close();
-            } catch (IOException e) {
+            } catch (Exception e) {
                 // Ignore
             }
         }
     }
 
-    // TODO: remove this class when sshd use lf->crlf conversion by default
-    public class LfToCrLfFilterOutputStream extends FilterOutputStream {
-
-        private boolean lastWasCr;
-
-        public LfToCrLfFilterOutputStream(OutputStream out) {
-            super(out);
-        }
-
-        @Override
-        public void write(int b) throws IOException {
-            if (!lastWasCr && b == '\n') {
-                out.write('\r');
-                out.write('\n');
-            } else {
-                out.write(b);
-            }
-            lastWasCr = b == '\r';
-        }
-
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshAction.java
----------------------------------------------------------------------
diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshAction.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshAction.java
index 46cd1b4..f704308 100644
--- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshAction.java
+++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshAction.java
@@ -34,8 +34,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import jline.UnixTerminal;
-import jline.internal.TerminalLineSettings;
 import org.apache.karaf.shell.api.action.Action;
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
@@ -64,6 +62,12 @@ import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
 import org.apache.sshd.common.util.io.NoCloseOutputStream;
+import org.jline.terminal.Attributes;
+import org.jline.terminal.Attributes.ControlChar;
+import org.jline.terminal.Attributes.InputFlag;
+import org.jline.terminal.Attributes.LocalFlag;
+import org.jline.terminal.Attributes.OutputFlag;
+import org.jline.terminal.Size;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -102,7 +106,6 @@ public class SshAction implements Action {
 
     @Override
     public Object execute() throws Exception {
-
         if (hostname.indexOf('@') >= 0) {
             if (username == null) {
                 username = hostname.substring(0, hostname.indexOf('@'));
@@ -153,7 +156,6 @@ public class SshAction implements Action {
             }
             @Override
             public void serverVersionInfo(ClientSession session, List<String> lines) {
-
             }
             @Override
             public String getUpdatedPassword(ClientSession session, String prompt, String lang) {
@@ -194,110 +196,83 @@ public class SshAction implements Action {
                     channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0);
                 } else if (session.getTerminal() != null) {
                     final ChannelShell channel = sshSession.createShellChannel();
-                    final jline.Terminal jlineTerminal = (jline.Terminal) session.get(".jline.terminal");
-                    if (jlineTerminal instanceof UnixTerminal) {
-                        TerminalLineSettings settings = ((UnixTerminal) jlineTerminal).getSettings();
+                    final org.jline.terminal.Terminal jlineTerminal = (org.jline.terminal.Terminal) session.get(".jline.terminal");
+                    Attributes attributes = jlineTerminal.enterRawMode();
+                    try {
                         Map<PtyMode, Integer> modes = new HashMap<>();
                         // Control chars
-                        modes.put(PtyMode.VINTR, settings.getProperty("vintr"));
-                        modes.put(PtyMode.VQUIT, settings.getProperty("vquit"));
-                        modes.put(PtyMode.VERASE, settings.getProperty("verase"));
-                        modes.put(PtyMode.VKILL, settings.getProperty("vkill"));
-                        modes.put(PtyMode.VEOF, settings.getProperty("veof"));
-                        modes.put(PtyMode.VEOL, settings.getProperty("veol"));
-                        modes.put(PtyMode.VEOL2, settings.getProperty("veol2"));
-                        modes.put(PtyMode.VSTART, settings.getProperty("vstart"));
-                        modes.put(PtyMode.VSTOP, settings.getProperty("vstop"));
-                        modes.put(PtyMode.VSUSP, settings.getProperty("vsusp"));
-                        modes.put(PtyMode.VDSUSP, settings.getProperty("vdusp"));
-                        modes.put(PtyMode.VREPRINT, settings.getProperty("vreprint"));
-                        modes.put(PtyMode.VWERASE, settings.getProperty("vwerase"));
-                        modes.put(PtyMode.VLNEXT, settings.getProperty("vlnext"));
-                        modes.put(PtyMode.VSTATUS, settings.getProperty("vstatus"));
-                        modes.put(PtyMode.VDISCARD, settings.getProperty("vdiscard"));
+                        modes.put(PtyMode.VINTR, attributes.getControlChar(ControlChar.VINTR));
+                        modes.put(PtyMode.VQUIT, attributes.getControlChar(ControlChar.VQUIT));
+                        modes.put(PtyMode.VERASE, attributes.getControlChar(ControlChar.VERASE));
+                        modes.put(PtyMode.VKILL, attributes.getControlChar(ControlChar.VKILL));
+                        modes.put(PtyMode.VEOF, attributes.getControlChar(ControlChar.VEOF));
+                        modes.put(PtyMode.VEOL, attributes.getControlChar(ControlChar.VEOL));
+                        modes.put(PtyMode.VEOL2, attributes.getControlChar(ControlChar.VEOL2));
+                        modes.put(PtyMode.VSTART, attributes.getControlChar(ControlChar.VSTART));
+                        modes.put(PtyMode.VSTOP, attributes.getControlChar(ControlChar.VSTOP));
+                        modes.put(PtyMode.VSUSP, attributes.getControlChar(ControlChar.VSUSP));
+                        modes.put(PtyMode.VDSUSP, attributes.getControlChar(ControlChar.VDSUSP));
+                        modes.put(PtyMode.VREPRINT, attributes.getControlChar(ControlChar.VREPRINT));
+                        modes.put(PtyMode.VWERASE, attributes.getControlChar(ControlChar.VWERASE));
+                        modes.put(PtyMode.VLNEXT, attributes.getControlChar(ControlChar.VLNEXT));
+                        modes.put(PtyMode.VSTATUS, attributes.getControlChar(ControlChar.VSTATUS));
+                        modes.put(PtyMode.VDISCARD, attributes.getControlChar(ControlChar.VDISCARD));
                         // Input flags
-                        modes.put(PtyMode.IGNPAR, getFlag(settings, PtyMode.IGNPAR));
-                        modes.put(PtyMode.PARMRK, getFlag(settings, PtyMode.PARMRK));
-                        modes.put(PtyMode.INPCK, getFlag(settings, PtyMode.INPCK));
-                        modes.put(PtyMode.ISTRIP, getFlag(settings, PtyMode.ISTRIP));
-                        modes.put(PtyMode.INLCR, getFlag(settings, PtyMode.INLCR));
-                        modes.put(PtyMode.IGNCR, getFlag(settings, PtyMode.IGNCR));
-                        modes.put(PtyMode.ICRNL, getFlag(settings, PtyMode.ICRNL));
-                        modes.put(PtyMode.IXON, getFlag(settings, PtyMode.IXON));
-                        modes.put(PtyMode.IXANY, getFlag(settings, PtyMode.IXANY));
-                        modes.put(PtyMode.IXOFF, getFlag(settings, PtyMode.IXOFF));
+                        modes.put(PtyMode.IGNPAR, getFlag(attributes, InputFlag.IGNPAR));
+                        modes.put(PtyMode.PARMRK, getFlag(attributes, InputFlag.PARMRK));
+                        modes.put(PtyMode.INPCK, getFlag(attributes, InputFlag.INPCK));
+                        modes.put(PtyMode.ISTRIP, getFlag(attributes, InputFlag.ISTRIP));
+                        modes.put(PtyMode.INLCR, getFlag(attributes, InputFlag.INLCR));
+                        modes.put(PtyMode.IGNCR, getFlag(attributes, InputFlag.IGNCR));
+                        modes.put(PtyMode.ICRNL, getFlag(attributes, InputFlag.ICRNL));
+                        modes.put(PtyMode.IXON, getFlag(attributes, InputFlag.IXON));
+                        modes.put(PtyMode.IXANY, getFlag(attributes, InputFlag.IXANY));
+                        modes.put(PtyMode.IXOFF, getFlag(attributes, InputFlag.IXOFF));
                         // Local flags
-                        modes.put(PtyMode.ISIG, getFlag(settings, PtyMode.ISIG));
-                        modes.put(PtyMode.ICANON, getFlag(settings, PtyMode.ICANON));
-                        modes.put(PtyMode.ECHO, getFlag(settings, PtyMode.ECHO));
-                        modes.put(PtyMode.ECHOE, getFlag(settings, PtyMode.ECHOE));
-                        modes.put(PtyMode.ECHOK, getFlag(settings, PtyMode.ECHOK));
-                        modes.put(PtyMode.ECHONL, getFlag(settings, PtyMode.ECHONL));
-                        modes.put(PtyMode.NOFLSH, getFlag(settings, PtyMode.NOFLSH));
-                        modes.put(PtyMode.TOSTOP, getFlag(settings, PtyMode.TOSTOP));
-                        modes.put(PtyMode.IEXTEN, getFlag(settings, PtyMode.IEXTEN));
+                        modes.put(PtyMode.ISIG, getFlag(attributes, LocalFlag.ISIG));
+                        modes.put(PtyMode.ICANON, getFlag(attributes, LocalFlag.ICANON));
+                        modes.put(PtyMode.ECHO, getFlag(attributes, LocalFlag.ECHO));
+                        modes.put(PtyMode.ECHOE, getFlag(attributes, LocalFlag.ECHOE));
+                        modes.put(PtyMode.ECHOK, getFlag(attributes, LocalFlag.ECHOK));
+                        modes.put(PtyMode.ECHONL, getFlag(attributes, LocalFlag.ECHONL));
+                        modes.put(PtyMode.NOFLSH, getFlag(attributes, LocalFlag.NOFLSH));
+                        modes.put(PtyMode.TOSTOP, getFlag(attributes, LocalFlag.TOSTOP));
+                        modes.put(PtyMode.IEXTEN, getFlag(attributes, LocalFlag.IEXTEN));
                         // Output flags
-                        modes.put(PtyMode.OPOST, getFlag(settings, PtyMode.OPOST));
-                        modes.put(PtyMode.OLCUC, getFlag(settings, PtyMode.OLCUC));
-                        modes.put(PtyMode.ONLCR, getFlag(settings, PtyMode.ONLCR));
-                        modes.put(PtyMode.OCRNL, getFlag(settings, PtyMode.OCRNL));
-                        modes.put(PtyMode.ONOCR, getFlag(settings, PtyMode.ONOCR));
-                        modes.put(PtyMode.ONLRET, getFlag(settings, PtyMode.ONLRET));
+                        modes.put(PtyMode.OPOST, getFlag(attributes, OutputFlag.OPOST));
+                        modes.put(PtyMode.ONLCR, getFlag(attributes, OutputFlag.ONLCR));
+                        modes.put(PtyMode.OCRNL, getFlag(attributes, OutputFlag.OCRNL));
+                        modes.put(PtyMode.ONOCR, getFlag(attributes, OutputFlag.ONOCR));
+                        modes.put(PtyMode.ONLRET, getFlag(attributes, OutputFlag.ONLRET));
                         channel.setPtyModes(modes);
-                    } else if (session.getTerminal() instanceof SshTerminal) {
-                        channel.setPtyModes(((SshTerminal) session.getTerminal()).getEnvironment().getPtyModes());
-                    } else {
-                        channel.setupSensibleDefaultPty();
-                    }
-                    channel.setPtyColumns(getTermWidth());
-                    channel.setPtyLines(getTermHeight());
-                    channel.setAgentForwarding(true);
-                    channel.setEnv("TERM", session.getTerminal().getType());
-                    Object ctype = session.get("LC_CTYPE");
-                    if (ctype != null) {
-                        channel.setEnv("LC_CTYPE", ctype.toString());
-                    }
-                    channel.setIn(new NoCloseInputStream(System.in));
-                    channel.setOut(new NoCloseOutputStream(System.out));
-                    channel.setErr(new NoCloseOutputStream(System.err));
-                    channel.open().verify();
-                    SignalListener signalListener = new SignalListener() {
-                        @Override
-                        public void signal(Signal signal) {
+                        channel.setPtyColumns(getTermWidth());
+                        channel.setPtyLines(getTermHeight());
+                        channel.setAgentForwarding(true);
+                        channel.setEnv("TERM", session.getTerminal().getType());
+                        Object ctype = session.get("LC_CTYPE");
+                        if (ctype != null) {
+                            channel.setEnv("LC_CTYPE", ctype.toString());
+                        }
+                        channel.setIn(new NoCloseInputStream(System.in));
+                        channel.setOut(new NoCloseOutputStream(System.out));
+                        channel.setErr(new NoCloseOutputStream(System.err));
+                        channel.open().verify();
+                        SignalListener signalListener = signal -> {
                             try {
-                                // Ugly hack to force the jline unix terminal to retrieve the width/height of the terminal
-                                // because results are cached for 1 second.
-                                try {
-                                    Field field = jlineTerminal.getClass().getSuperclass().getDeclaredField("settings");
-                                    field.setAccessible(true);
-                                    Object settings = field.get(jlineTerminal);
-                                    field = settings.getClass().getDeclaredField("configLastFetched");
-                                    field.setAccessible(true);
-                                    field.setLong(settings, 0L);
-                                } catch (Throwable t) {
-                                    // Ignore
-                                }
-                                // TODO: replace with PtyCapableChannelSession#sendWindowChange
-                                org.apache.sshd.common.session.Session sshSession = channel.getSession();
-                                Buffer buffer = sshSession.createBuffer(SshConstants.SSH_MSG_CHANNEL_REQUEST);
-                                buffer.putInt(channel.getRecipient());
-                                buffer.putString("window-change");
-                                buffer.putBoolean(false);
-                                buffer.putInt(session.getTerminal().getWidth());
-                                buffer.putInt(session.getTerminal().getHeight());
-                                buffer.putInt(0);
-                                buffer.putInt(0);
-                                sshSession.writePacket(buffer);
+                                Size size = jlineTerminal.getSize();
+                                channel.sendWindowChange(size.getColumns(), size.getRows());
                             } catch (IOException e) {
                                 // Ignore
                             }
+                        };
+                        session.getTerminal().addSignalListener(signalListener, Signal.WINCH);
+                        try {
+                            channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0);
+                        } finally {
+                            session.getTerminal().removeSignalListener(signalListener);
                         }
-                    };
-                    session.getTerminal().addSignalListener(signalListener, Signal.WINCH);
-                    try {
-                        channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0);
                     } finally {
-                        session.getTerminal().removeSignalListener(signalListener);
+                        jlineTerminal.setAttributes(attributes);
                     }
                 } else {
                     throw new IllegalStateException("No terminal for interactive ssh session");
@@ -313,11 +288,19 @@ public class SshAction implements Action {
         return null;
     }
 
-    private int getFlag(TerminalLineSettings settings, PtyMode mode) {
-        String name = mode.toString().toLowerCase();
-        return (settings.getPropertyAsString(name) != null) ? 1 : 0;
+    private static int getFlag(Attributes attributes, InputFlag flag) {
+        return attributes.getInputFlag(flag) ? 1 : 0;
+    }
+
+    private static int getFlag(Attributes attributes, OutputFlag flag) {
+        return attributes.getOutputFlag(flag) ? 1 : 0;
     }
 
+    private static int getFlag(Attributes attributes, LocalFlag flag) {
+        return attributes.getLocalFlag(flag) ? 1 : 0;
+    }
+
+
     private int getTermWidth() {
         Terminal term = session.getTerminal();
         return term != null ? term.getWidth() : 80;

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshTerminal.java
----------------------------------------------------------------------
diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshTerminal.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshTerminal.java
index 4b7aff6..ebd9e22 100644
--- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshTerminal.java
+++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshTerminal.java
@@ -18,53 +18,164 @@
  */
 package org.apache.karaf.shell.ssh;
 
-import org.apache.karaf.shell.api.console.Signal;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.EnumSet;
+import java.util.Map;
+
+import org.apache.karaf.shell.api.console.SignalListener;
 import org.apache.karaf.shell.api.console.Terminal;
-import org.apache.karaf.shell.support.terminal.SignalSupport;
 import org.apache.sshd.common.channel.PtyMode;
 import org.apache.sshd.server.Environment;
+import org.jline.terminal.Attributes.ControlChar;
+import org.jline.terminal.Attributes.InputFlag;
+import org.jline.terminal.Attributes.LocalFlag;
+import org.jline.terminal.Attributes.OutputFlag;
+import org.jline.terminal.Size;
+import org.jline.terminal.impl.ExternalTerminal;
 
-public class SshTerminal extends SignalSupport implements Terminal {
+public class SshTerminal extends ExternalTerminal implements Terminal {
 
     private Environment environment;
-    private boolean echo;
 
-    public SshTerminal(Environment environment) {
+    public SshTerminal(Environment environment, InputStream input, OutputStream output) throws IOException {
+        super("Karaf SSH terminal",
+              environment.getEnv().get(Environment.ENV_TERM),
+              input,
+              output,
+              "UTF-8");
         this.environment = environment;
-        this.environment.addSignalListener(new org.apache.sshd.server.SignalListener() {
-            @Override
-            public void signal(org.apache.sshd.server.Signal signal) {
-                SshTerminal.this.signal(Signal.WINCH);
+        this.environment.addSignalListener(this::handleSignal);
+        for (Map.Entry<PtyMode, Integer> e : environment.getPtyModes().entrySet()) {
+            switch (e.getKey()) {
+                case VINTR:
+                    attributes.setControlChar(ControlChar.VINTR, e.getValue());
+                    break;
+                case VQUIT:
+                    attributes.setControlChar(ControlChar.VQUIT, e.getValue());
+                    break;
+                case VERASE:
+                    attributes.setControlChar(ControlChar.VERASE, e.getValue());
+                    break;
+                case VKILL:
+                    attributes.setControlChar(ControlChar.VKILL, e.getValue());
+                    break;
+                case VEOF:
+                    attributes.setControlChar(ControlChar.VEOF, e.getValue());
+                    break;
+                case VEOL:
+                    attributes.setControlChar(ControlChar.VEOL, e.getValue());
+                    break;
+                case VEOL2:
+                    attributes.setControlChar(ControlChar.VEOL2, e.getValue());
+                    break;
+                case VSTART:
+                    attributes.setControlChar(ControlChar.VSTART, e.getValue());
+                    break;
+                case VSTOP:
+                    attributes.setControlChar(ControlChar.VSTOP, e.getValue());
+                    break;
+                case VSUSP:
+                    attributes.setControlChar(ControlChar.VSUSP, e.getValue());
+                    break;
+                case VDSUSP:
+                    attributes.setControlChar(ControlChar.VDSUSP, e.getValue());
+                    break;
+                case VREPRINT:
+                    attributes.setControlChar(ControlChar.VREPRINT, e.getValue());
+                    break;
+                case VWERASE:
+                    attributes.setControlChar(ControlChar.VWERASE, e.getValue());
+                    break;
+                case VLNEXT:
+                    attributes.setControlChar(ControlChar.VLNEXT, e.getValue());
+                    break;
+                case VSTATUS:
+                    attributes.setControlChar(ControlChar.VSTATUS, e.getValue());
+                    break;
+                case VDISCARD:
+                    attributes.setControlChar(ControlChar.VDISCARD, e.getValue());
+                    break;
+                case ECHO:
+                    attributes.setLocalFlag(LocalFlag.ECHO, e.getValue() != 0);
+                    break;
+                case ICANON:
+                    attributes.setLocalFlag(LocalFlag.ICANON, e.getValue() != 0);
+                    break;
+                case ISIG:
+                    attributes.setLocalFlag(LocalFlag.ISIG, e.getValue() != 0);
+                    break;
+                case ICRNL:
+                    attributes.setInputFlag(InputFlag.ICRNL, e.getValue() != 0);
+                    break;
+                case INLCR:
+                    attributes.setInputFlag(InputFlag.INLCR, e.getValue() != 0);
+                    break;
+                case IGNCR:
+                    attributes.setInputFlag(InputFlag.IGNCR, e.getValue() != 0);
+                    break;
+                case OCRNL:
+                    attributes.setOutputFlag(OutputFlag.OCRNL, e.getValue() != 0);
+                    break;
+                case ONLCR:
+                    attributes.setOutputFlag(OutputFlag.ONLCR, e.getValue() != 0);
+                    break;
+                case ONLRET:
+                    attributes.setOutputFlag(OutputFlag.ONLRET, e.getValue() != 0);
+                    break;
+                case OPOST:
+                    attributes.setOutputFlag(OutputFlag.OPOST, e.getValue() != 0);
+                    break;
             }
-        }, org.apache.sshd.server.Signal.WINCH);
-        this.echo = environment.getPtyModes().containsKey(PtyMode.ECHO);
+        }
     }
 
-    @Override
-    public String getType() {
-        return environment.getEnv().get(Environment.ENV_TERM);
+    protected void handleSignal(org.apache.sshd.server.Signal signal) {
+        if (signal == org.apache.sshd.server.Signal.INT) {
+            raise(Signal.INT);
+        } else if (signal == org.apache.sshd.server.Signal.QUIT) {
+            raise(Signal.QUIT);
+        } else if (signal == org.apache.sshd.server.Signal.TSTP) {
+            raise(Signal.TSTP);
+        } else if (signal == org.apache.sshd.server.Signal.CONT) {
+            raise(Signal.CONT);
+        } else if (signal == org.apache.sshd.server.Signal.WINCH) {
+            int w = Integer.valueOf(this.environment.getEnv().get(Environment.ENV_COLUMNS));
+            int h = Integer.valueOf(this.environment.getEnv().get(Environment.ENV_LINES));
+            setSize(new Size(w, h));
+            raise(Signal.WINCH);
+        }
     }
 
     @Override
     public int getWidth() {
-        int width = 0;
-        try {
-            width = Integer.valueOf(this.environment.getEnv().get(Environment.ENV_COLUMNS));
-        } catch (Throwable t) {
-            // Ignore
-        }
-        return width > 0 ? width : 80;
+        return size.getColumns();
     }
 
     @Override
     public int getHeight() {
-        int height = 0;
-        try {
-            height = Integer.valueOf(this.environment.getEnv().get(Environment.ENV_LINES));
-        } catch (Throwable t) {
-            // Ignore
-        }
-        return height > 0 ? height : 24;
+        return size.getRows();
+    }
+
+    @Override
+    public void addSignalListener(SignalListener listener) {
+        // TODO:JLINE
+    }
+
+    @Override
+    public void addSignalListener(SignalListener listener, org.apache.karaf.shell.api.console.Signal... signal) {
+        // TODO:JLINE
+    }
+
+    @Override
+    public void addSignalListener(SignalListener listener, EnumSet<org.apache.karaf.shell.api.console.Signal> signals) {
+        // TODO:JLINE
+    }
+
+    @Override
+    public void removeSignalListener(SignalListener listener) {
+        // TODO:JLINE
     }
 
     @Override
@@ -74,16 +185,12 @@ public class SshTerminal extends SignalSupport implements Terminal {
 
     @Override
     public boolean isEchoEnabled() {
-        return echo;
+        return echo();
     }
 
     @Override
     public void setEchoEnabled(boolean enabled) {
-        echo = enabled;
-    }
-
-    public Environment getEnvironment() {
-        return environment;
+        echo(enabled);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/util/src/main/java/org/apache/karaf/util/jaas/JaasHelper.java
----------------------------------------------------------------------
diff --git a/util/src/main/java/org/apache/karaf/util/jaas/JaasHelper.java b/util/src/main/java/org/apache/karaf/util/jaas/JaasHelper.java
index a4d2f13..2cb96ca 100644
--- a/util/src/main/java/org/apache/karaf/util/jaas/JaasHelper.java
+++ b/util/src/main/java/org/apache/karaf/util/jaas/JaasHelper.java
@@ -73,6 +73,14 @@ public class JaasHelper {
         return false;
     }
 
+    public static void runAs(final Subject subject,
+                             final Runnable action) {
+        if (action == null) {
+            throw new NullPointerException();
+        }
+        doAs(subject, (PrivilegedAction<Object>)(() -> { action.run(); return null; } ));
+    }
+
     public static <T> T doAs(final Subject subject,
                              final PrivilegedAction<T> action) {
         if (action == null) {

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/webconsole/gogo/pom.xml
----------------------------------------------------------------------
diff --git a/webconsole/gogo/pom.xml b/webconsole/gogo/pom.xml
index 83d40b6..566f272 100644
--- a/webconsole/gogo/pom.xml
+++ b/webconsole/gogo/pom.xml
@@ -83,6 +83,10 @@
             <artifactId>org.apache.karaf.shell.console</artifactId>
         </dependency>
         <dependency>
+            <groupId>jline</groupId>
+            <artifactId>jline</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.apache.karaf</groupId>
             <artifactId>org.apache.karaf.util</artifactId>
             <scope>provided</scope>
@@ -122,7 +126,6 @@
                 <configuration>
                     <instructions>
                         <Import-Package>
-                            jline;version="[${jline.version}, ${jline.version}]",
 			                org.apache.felix.webconsole*;version="[3,5)",
                             *
                         </Import-Package>

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/GogoPlugin.java
----------------------------------------------------------------------
diff --git a/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/GogoPlugin.java b/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/GogoPlugin.java
index e6e826b..7b285c1 100644
--- a/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/GogoPlugin.java
+++ b/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/GogoPlugin.java
@@ -26,6 +26,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.InterruptedIOException;
+import java.io.OutputStream;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
 import java.io.PrintStream;
@@ -183,13 +184,15 @@ public class GogoPlugin extends AbstractWebConsolePlugin {
 
                 in = new PipedOutputStream();
                 out = new PipedInputStream();
-                PrintStream pipedOut = new PrintStream(new PipedOutputStream(out), true);
+                InputStream input = new PipedInputStream(in);
+                OutputStream output = new PipedOutputStream(out);
+                PrintStream pipedOut = new PrintStream(output, true);
                 
                 Session session = sessionFactory.create(
-                        new PipedInputStream(in),
+                        input,
                         pipedOut,
                         pipedOut,
-                        new WebTerminal(TERM_WIDTH, TERM_HEIGHT),
+                        new WebTerminal(TERM_WIDTH, TERM_HEIGHT, input, pipedOut),
                         null,
                         null);
                 new Thread(session, "Karaf web console user " + getCurrentUserName()).start();

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/WebTerminal.java
----------------------------------------------------------------------
diff --git a/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/WebTerminal.java b/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/WebTerminal.java
index 4ac8923..e7fe2a8 100644
--- a/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/WebTerminal.java
+++ b/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/WebTerminal.java
@@ -16,46 +16,67 @@
  */
 package org.apache.karaf.webconsole.gogo;
 
+ import java.io.IOException;
+ import java.io.InputStream;
+ import java.io.OutputStream;
+ import java.util.EnumSet;
+
+ import org.apache.karaf.shell.api.console.Signal;
+ import org.apache.karaf.shell.api.console.SignalListener;
  import org.apache.karaf.shell.api.console.Terminal;
- import org.apache.karaf.shell.support.terminal.SignalSupport;
+ import org.jline.terminal.impl.ExternalTerminal;
 
-public class WebTerminal extends SignalSupport implements Terminal {
+public class WebTerminal extends ExternalTerminal implements Terminal {
 
-    private int width;
-    private int height;
-    private boolean echo = true;
+    public WebTerminal(int width, int height, InputStream input, OutputStream output) throws IOException {
+        super("Karaf Web Terminal", "ansi", input, output, "UTF-8");
+        size.setColumns(width);
+        size.setRows(height);
+    }
 
-    public WebTerminal(int width, int height) {
-        this.width = width;
-        this.height = height;
+    @Override
+    public int getWidth() {
+        return size.getColumns();
     }
 
     @Override
-    public String getType() {
-        return "ansi";
+    public int getHeight() {
+        return size.getRows();
     }
 
     @Override
-    public boolean isAnsiSupported() {
-        return true;
+    public void addSignalListener(SignalListener listener) {
+        // TODO:JLINE
     }
 
     @Override
-    public boolean isEchoEnabled() {
-        return echo;
+    public void addSignalListener(SignalListener listener, org.apache.karaf.shell.api.console.Signal... signal) {
+        // TODO:JLINE
     }
 
     @Override
-    public void setEchoEnabled(boolean enabled) {
-        echo = enabled;
+    public void addSignalListener(SignalListener listener, EnumSet<org.apache.karaf.shell.api.console.Signal> signals) {
+        // TODO:JLINE
     }
 
-    public int getWidth() {
-        return width;
+    @Override
+    public void removeSignalListener(SignalListener listener) {
+        // TODO:JLINE
     }
 
-    public int getHeight() {
-        return height;
+    @Override
+    public boolean isAnsiSupported() {
+        return true;
+    }
+
+    @Override
+    public boolean isEchoEnabled() {
+        return echo();
+    }
+
+    @Override
+    public void setEchoEnabled(boolean enabled) {
+        echo(enabled);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/b4ae5245/wrapper/src/main/java/org/apache/karaf/wrapper/internal/WrapperServiceImpl.java
----------------------------------------------------------------------
diff --git a/wrapper/src/main/java/org/apache/karaf/wrapper/internal/WrapperServiceImpl.java b/wrapper/src/main/java/org/apache/karaf/wrapper/internal/WrapperServiceImpl.java
index a4832c8..492a479 100644
--- a/wrapper/src/main/java/org/apache/karaf/wrapper/internal/WrapperServiceImpl.java
+++ b/wrapper/src/main/java/org/apache/karaf/wrapper/internal/WrapperServiceImpl.java
@@ -16,8 +16,8 @@
  */
 package org.apache.karaf.wrapper.internal;
 
+import org.apache.karaf.shell.support.ansi.SimpleAnsi;
 import org.apache.karaf.wrapper.WrapperService;
-import org.fusesource.jansi.Ansi;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -302,8 +302,8 @@ public class WrapperServiceImpl implements WrapperService {
     private void mkdir(File file) {
         if (!file.exists()) {
             LOGGER.info("Creating missing directory: {}", file.getPath());
-            System.out.println(Ansi.ansi().a("Creating missing directory: ")
-                    .a(Ansi.Attribute.INTENSITY_BOLD).a(file.getPath()).a(Ansi.Attribute.RESET).toString());
+            System.out.println("Creating missing directory: "
+                    + SimpleAnsi.INTENSITY_BOLD + file.getPath() + SimpleAnsi.INTENSITY_NORMAL);
             file.mkdirs();
         }
     }
@@ -311,8 +311,8 @@ public class WrapperServiceImpl implements WrapperService {
     private void copyResourceTo(File outFile, String resource, boolean text) throws Exception {
         if (!outFile.exists()) {
             LOGGER.info("Creating file: {}", outFile.getPath());
-            System.out.println(Ansi.ansi().a("Creating file: ")
-                    .a(Ansi.Attribute.INTENSITY_BOLD).a(outFile.getPath()).a(Ansi.Attribute.RESET).toString());
+            System.out.println("Creating file: "
+                    + SimpleAnsi.INTENSITY_BOLD + outFile.getPath() + SimpleAnsi.INTENSITY_NORMAL);
             InputStream is = WrapperServiceImpl.class.getResourceAsStream(resource);
             if (is == null) {
                 throw new IllegalArgumentException("Resource " + resource + " doesn't exist");
@@ -348,17 +348,17 @@ public class WrapperServiceImpl implements WrapperService {
             }
         } else {
             LOGGER.warn("File already exists. Move it out of the way if you wish to recreate it: {}", outFile.getPath());
-            System.out.println(Ansi.ansi()
-                    .fg(Ansi.Color.RED).a("File already exists").a(Ansi.Attribute.RESET)
-                    .a(". Move it out of the way if you wish to recreate it: ").a(outFile.getPath()).toString());
+            System.out.println(
+                    SimpleAnsi.COLOR_RED + "File already exists" + SimpleAnsi.COLOR_DEFAULT
+                            + ". Move it out of the way if you wish to recreate it: " + outFile.getPath());
         }
     }
 
     private void copyFilteredResourceTo(File outFile, String resource, HashMap<String, String> props, String[] envs, String[] includes) throws Exception {
         if (!outFile.exists()) {
             LOGGER.info("Creating file: {}", outFile.getPath());
-            System.out.println(Ansi.ansi().a("Creating file: ")
-                    .a(Ansi.Attribute.INTENSITY_BOLD).a(outFile.getPath()).a(Ansi.Attribute.RESET).toString());
+            System.out.println("Creating file: "
+                    + SimpleAnsi.INTENSITY_BOLD + outFile.getPath() + SimpleAnsi.INTENSITY_NORMAL);
             InputStream is = WrapperServiceImpl.class.getResourceAsStream(resource);
             if (is == null) {
                 throw new IllegalArgumentException("Resource " + resource + " doesn't exist");
@@ -393,9 +393,9 @@ public class WrapperServiceImpl implements WrapperService {
             }
         } else {
             LOGGER.warn("File already exists. Move it out of the way if you wish to recreate it: {}", outFile.getPath());
-            System.out.println(Ansi.ansi()
-                    .fg(Ansi.Color.RED).a("File already exists").a(Ansi.Attribute.RESET)
-                    .a(". Move it out of the way if you wish to recreate it: ").a(outFile.getPath()).toString());
+            System.out.println(
+                    SimpleAnsi.COLOR_RED + "File already exists" + SimpleAnsi.COLOR_DEFAULT
+                            + ". Move it out of the way if you wish to recreate it: " + outFile.getPath());
         }
     }
 
@@ -441,8 +441,8 @@ public class WrapperServiceImpl implements WrapperService {
     private void createJar(File outFile, String resource) throws Exception {
         if (!outFile.exists()) {
             LOGGER.info("Creating file: {}", outFile.getPath());
-            System.out.println(Ansi.ansi().a("Creating file: ")
-                    .a(Ansi.Attribute.INTENSITY_BOLD).a(outFile.getPath()).a(Ansi.Attribute.RESET).toString());
+            System.out.println("Creating file: "
+                    + SimpleAnsi.INTENSITY_BOLD + outFile.getPath() + SimpleAnsi.INTENSITY_NORMAL);
             InputStream is = getClass().getClassLoader().getResourceAsStream(resource);
             if (is == null) {
                 throw new IllegalStateException("Resource " + resource + " not found!");