Repository: karaf
Updated Branches:
  refs/heads/master e5e50310a -> 57ec802bc


[KARAF-2983] Support window size change signals in Terminal

Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/a5450aa2
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/a5450aa2
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/a5450aa2

Branch: refs/heads/master
Commit: a5450aa241cb6e15d430ad4792d924f2636bd3f1
Parents: e5e5031
Author: Guillaume Nodet <[email protected]>
Authored: Thu May 15 19:09:32 2014 +0200
Committer: Guillaume Nodet <[email protected]>
Committed: Thu May 15 19:09:32 2014 +0200

----------------------------------------------------------------------
 .../apache/karaf/shell/api/console/Signal.java  | 81 +++++++++++++++++
 .../karaf/shell/api/console/SignalListener.java | 31 +++++++
 .../karaf/shell/api/console/Terminal.java       | 27 ++++++
 .../shell/impl/console/ConsoleSessionImpl.java  |  8 ++
 .../karaf/shell/impl/console/JLineTerminal.java | 57 +++++++++++-
 .../impl/console/osgi/LocalConsoleManager.java  |  5 +-
 .../shell/support/terminal/SignalSupport.java   | 96 ++++++++++++++++++++
 .../org/apache/karaf/shell/ssh/SshTerminal.java | 12 ++-
 .../karaf/webconsole/gogo/WebTerminal.java      |  3 +-
 9 files changed, 311 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/a5450aa2/shell/core/src/main/java/org/apache/karaf/shell/api/console/Signal.java
----------------------------------------------------------------------
diff --git 
a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Signal.java 
b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Signal.java
new file mode 100644
index 0000000..1e4a563
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Signal.java
@@ -0,0 +1,81 @@
+/*
+ * 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.api.console;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum Signal {
+
+    HUP(1),
+    INT(2),
+    QUIT(3),
+    ILL(4),
+    TRAP(5),
+    IOT(6),
+    BUS(7),
+    FPE(8),
+    KILL(9),
+    USR1(10),
+    SEGV(11),
+    USR2(12),
+    PIPE(13),
+    ALRM(14),
+    TERM(15),
+    STKFLT(16),
+    CHLD(17),
+    CONT(18),
+    STOP(19),
+    TSTP(20),
+    TTIN(21),
+    TTOU(22),
+    URG(23),
+    XCPU(24),
+    XFSZ(25),
+    VTALRM(26),
+    PROF(27),
+    WINCH(28),
+    IO(29),
+    PWR(30);
+
+    private static final Map<String, Signal> lookupTable = new HashMap<String, 
Signal>(40);
+
+    static {
+        // registering the signals in the lookup table to allow faster
+        // string based signal lookups
+        for (Signal s : Signal.values()) {
+            lookupTable.put(s.name(), s);
+        }
+    }
+
+    public static Signal get(String name) {
+        return lookupTable.get(name);
+    }
+
+    private final int numeric;
+
+    private Signal(int numeric) {
+        this.numeric = numeric;
+    }
+
+    public int getNumeric() {
+        return numeric;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/a5450aa2/shell/core/src/main/java/org/apache/karaf/shell/api/console/SignalListener.java
----------------------------------------------------------------------
diff --git 
a/shell/core/src/main/java/org/apache/karaf/shell/api/console/SignalListener.java
 
b/shell/core/src/main/java/org/apache/karaf/shell/api/console/SignalListener.java
new file mode 100644
index 0000000..3f6927a
--- /dev/null
+++ 
b/shell/core/src/main/java/org/apache/karaf/shell/api/console/SignalListener.java
@@ -0,0 +1,31 @@
+/*
+ * 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.api.console;
+
+/**
+ * Define a listener to receive signals
+ */
+public interface SignalListener {
+
+    /**
+     *
+     * @param signal
+     */
+    void signal(Signal signal);
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/a5450aa2/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java
----------------------------------------------------------------------
diff --git 
a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java 
b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java
index 3549ec7..c0ee911 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java
@@ -18,6 +18,8 @@
  */
 package org.apache.karaf.shell.api.console;
 
+import java.util.EnumSet;
+
 /**
  * Session terminal.
  */
@@ -48,4 +50,29 @@ public interface Terminal {
      */
     void setEchoEnabled(boolean enabled);
 
+    /**
+     * Add a qualified listener for the specific signal
+     * @param listener the listener to register
+     * @param signal the signal the listener is interested in
+     */
+    void addSignalListener(SignalListener listener, Signal... signal);
+
+    /**
+     * Add a qualified listener for the specific set of signal
+     * @param listener the listener to register
+     * @param signals the signals the listener is interested in
+     */
+    void addSignalListener(SignalListener listener, EnumSet<Signal> signals);
+
+    /**
+     * Add a global listener for all signals
+     * @param listener the listener to register
+     */
+    void addSignalListener(SignalListener listener);
+
+    /**
+     * Remove a previously registered listener for all the signals it was 
registered
+     * @param listener the listener to remove
+     */
+    void removeSignalListener(SignalListener listener);
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/a5450aa2/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
----------------------------------------------------------------------
diff --git 
a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
 
b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
index 1673f37..dbfc59a 100644
--- 
a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
+++ 
b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
@@ -19,6 +19,7 @@
 package org.apache.karaf.shell.impl.console;
 
 import java.io.CharArrayWriter;
+import java.io.Closeable;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -233,6 +234,13 @@ public class ConsoleSessionImpl implements Session {
         if (closeCallback != null) {
             closeCallback.run();
         }
+        if (terminal instanceof Closeable) {
+            try {
+                ((Closeable) terminal).close();
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
     }
 
     public void run() {

http://git-wip-us.apache.org/repos/asf/karaf/blob/a5450aa2/shell/core/src/main/java/org/apache/karaf/shell/impl/console/JLineTerminal.java
----------------------------------------------------------------------
diff --git 
a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/JLineTerminal.java
 
b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/JLineTerminal.java
index d000885..aaf4eb6 100644
--- 
a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/JLineTerminal.java
+++ 
b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/JLineTerminal.java
@@ -18,17 +18,26 @@
  */
 package org.apache.karaf.shell.impl.console;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.karaf.shell.api.console.Signal;
 import org.apache.karaf.shell.api.console.Terminal;
+import org.apache.karaf.shell.support.terminal.SignalSupport;
 
 /**
-* Created by gnodet on 27/02/14.
-*/
-public class JLineTerminal implements Terminal {
+ * Created by gnodet on 27/02/14.
+ */
+public class JLineTerminal extends SignalSupport implements Terminal, 
Closeable {
 
     private final jline.Terminal terminal;
 
     public JLineTerminal(jline.Terminal terminal) {
         this.terminal = terminal;
+        registerSignalHandler();
     }
 
     public jline.Terminal getTerminal() {
@@ -59,4 +68,46 @@ public class JLineTerminal implements Terminal {
     public void setEchoEnabled(boolean enabled) {
         terminal.setEchoEnabled(enabled);
     }
+
+    @Override
+    public void close() throws IOException {
+        unregisterSignalHandler();
+    }
+
+    private void registerSignalHandler() {
+        try {
+            Class<?> signalClass = Class.forName("sun.misc.Signal");
+            Class<?> signalHandlerClass = 
Class.forName("sun.misc.SignalHandler");
+            // Implement signal handler
+            Object signalHandler = 
Proxy.newProxyInstance(getClass().getClassLoader(),
+                    new Class<?>[]{signalHandlerClass}, new 
InvocationHandler() {
+                        public Object invoke(Object proxy, Method method, 
Object[] args) throws Throwable {
+                            JLineTerminal.this.signal(Signal.WINCH);
+                            return null;
+                        }
+                    }
+            );
+            // Register the signal handler, this code is equivalent to:
+            // Signal.handle(new Signal("CONT"), signalHandler);
+            signalClass.getMethod("handle", signalClass, 
signalHandlerClass).invoke(null, 
signalClass.getConstructor(String.class).newInstance("WINCH"), signalHandler);
+        } catch (Exception e) {
+            // Ignore this exception, if the above failed, the signal API is 
incompatible with what we're expecting
+
+        }
+    }
+
+    private void unregisterSignalHandler() {
+        try {
+            Class<?> signalClass = Class.forName("sun.misc.Signal");
+            Class<?> signalHandlerClass = 
Class.forName("sun.misc.SignalHandler");
+
+            Object signalHandler = 
signalHandlerClass.getField("SIG_DFL").get(null);
+            // Register the signal handler, this code is equivalent to:
+            // Signal.handle(new Signal("CONT"), signalHandler);
+            signalClass.getMethod("handle", signalClass, 
signalHandlerClass).invoke(null, 
signalClass.getConstructor(String.class).newInstance("WINCH"), signalHandler);
+        } catch (Exception e) {
+            // Ignore this exception, if the above failed, the signal API is 
incompatible with what we're expecting
+
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/a5450aa2/shell/core/src/main/java/org/apache/karaf/shell/impl/console/osgi/LocalConsoleManager.java
----------------------------------------------------------------------
diff --git 
a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/osgi/LocalConsoleManager.java
 
b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/osgi/LocalConsoleManager.java
index 1c30dd0..e93d93e 100644
--- 
a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/osgi/LocalConsoleManager.java
+++ 
b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/osgi/LocalConsoleManager.java
@@ -67,7 +67,7 @@ public class LocalConsoleManager {
 
         
         final Subject subject = createLocalKarafSubject();    
-        this.session = JaasHelper.<Session>doAs(subject, new 
PrivilegedAction<Session>() {
+        this.session = JaasHelper.doAs(subject, new 
PrivilegedAction<Session>() {
             public Session run() {
                 String encoding = getEncoding();
                 session = sessionFactory.create(
@@ -95,8 +95,7 @@ public class LocalConsoleManager {
     }
 
     private String getEncoding() {
-        String ctype = System.getenv("LC_CTYPE");
-        String encoding = ctype;
+        String encoding = System.getenv("LC_CTYPE");
         if (encoding != null && encoding.indexOf('.') > 0) {
             encoding = encoding.substring(encoding.indexOf('.') + 1);
         } else {

http://git-wip-us.apache.org/repos/asf/karaf/blob/a5450aa2/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
new file mode 100644
index 0000000..39ff0f7
--- /dev/null
+++ 
b/shell/core/src/main/java/org/apache/karaf/shell/support/terminal/SignalSupport.java
@@ -0,0 +1,96 @@
+/*
+ * 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.support.terminal;
+
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.karaf.shell.api.console.Signal;
+import org.apache.karaf.shell.api.console.SignalListener;
+
+public class SignalSupport {
+
+    private final Map<Signal, Set<SignalListener>> listeners;
+
+    public SignalSupport() {
+        listeners = new ConcurrentHashMap<>(3);
+    }
+
+    public void addSignalListener(SignalListener listener, Signal... signals) {
+        if (signals == null) {
+            throw new IllegalArgumentException("signals may not be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("listener may not be null");
+        }
+        for (Signal s : signals) {
+            getSignalListeners(s, true).add(listener);
+        }
+    }
+
+    public void addSignalListener(SignalListener listener) {
+        addSignalListener(listener, EnumSet.allOf(Signal.class));
+    }
+
+    public void addSignalListener(SignalListener listener, EnumSet<Signal> 
signals) {
+        if (signals == null) {
+            throw new IllegalArgumentException("signals may not be null");
+        }
+        addSignalListener(listener, signals.toArray(new 
Signal[signals.size()]));
+    }
+
+    public void removeSignalListener(SignalListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener may not be null");
+        }
+        for (Signal s : EnumSet.allOf(Signal.class)) {
+            final Set<SignalListener> ls = getSignalListeners(s, false);
+            if (ls != null) {
+                ls.remove(listener);
+            }
+        }
+    }
+
+    public void signal(Signal signal) {
+        final Set<SignalListener> ls = getSignalListeners(signal, false);
+        if (ls != null) {
+            for (SignalListener l : ls) {
+                l.signal(signal);
+            }
+        }
+    }
+
+    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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/a5450aa2/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 fa24169..17291d0 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,16 +18,23 @@
  */
 package org.apache.karaf.shell.ssh;
 
+import org.apache.karaf.shell.api.console.Signal;
 import org.apache.karaf.shell.api.console.Terminal;
+import org.apache.karaf.shell.support.terminal.SignalSupport;
 import org.apache.sshd.server.Environment;
 
-public class SshTerminal implements Terminal {
+public class SshTerminal extends SignalSupport implements Terminal {
 
     private Environment environment;
 
-
     public SshTerminal(Environment environment) {
         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);
+            }
+        }, org.apache.sshd.server.Signal.WINCH);
     }
 
     @Override
@@ -66,4 +73,5 @@ public class SshTerminal implements Terminal {
     public void setEchoEnabled(boolean enabled) {
         // TODO: how to disable echo over ssh ?
     }
+
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/a5450aa2/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 865a528..f0dd828 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
@@ -17,8 +17,9 @@
 package org.apache.karaf.webconsole.gogo;
 
  import org.apache.karaf.shell.api.console.Terminal;
+ import org.apache.karaf.shell.support.terminal.SignalSupport;
 
-public class WebTerminal implements Terminal {
+public class WebTerminal extends SignalSupport implements Terminal {
 
     private int width;
     private int height;

Reply via email to