/* * 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.felix.webconsole.plugins.gogo.impl; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.PrintStream; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import org.apache.felix.service.command.CommandProcessor; import org.apache.felix.service.command.CommandSession; public class Console implements Runnable { public static final String IGNORE_INTERRUPTS = "karaf.ignoreInterrupts"; protected CommandSession session; private BlockingQueue<Integer> queue; private boolean interrupt; private Thread pipe; volatile private boolean running; volatile private boolean eof; private Runnable closeCallback; private InputStream consoleInput; private InputStream in; private PrintStream out; private PrintStream err; private Thread thread; public Console(CommandProcessor processor, InputStream in, PrintStream out, PrintStream err, Runnable closeCallback, Map<String, String> sessionProps) throws Exception { this.in = in; this.out = out; this.err = err; this.queue = new ArrayBlockingQueue<Integer>(1024); this.consoleInput = new ConsoleInputStream(); this.session = processor.createSession(this.consoleInput, this.out, this.err); this.closeCallback = closeCallback; if (sessionProps != null) { for (Entry<String, String> entry: sessionProps.entrySet()) { this.session.put(entry.getKey(), entry.getValue()); } } pipe = new Thread(new Pipe()); pipe.setName("gogo shell pipe thread"); pipe.setDaemon(true); } public CommandSession getSession() { return session; } public void close() { running = false; pipe.interrupt(); } public void run() { thread = Thread.currentThread(); running = true; pipe.start(); try { session.execute("gosh --login --noshutdown"); } catch (Exception e) { e.printStackTrace(this.err); } finally { session.close(); this.out.println("Good Bye!"); } close(); if (closeCallback != null) { closeCallback.run(); } } protected boolean getBoolean(String name) { Object s = session.get(name); if (s == null) { s = System.getProperty(name); } if (s == null) { return false; } if (s instanceof Boolean) { return (Boolean) s; } return Boolean.parseBoolean(s.toString()); } private void checkInterrupt() throws IOException { if (Thread.interrupted() || interrupt) { interrupt = false; throw new InterruptedIOException("Keyboard interruption"); } } private void interrupt() { interrupt = true; thread.interrupt(); } private class ConsoleInputStream extends InputStream { private int read(boolean wait) throws IOException { if (!running) { return -1; } checkInterrupt(); if (eof && queue.isEmpty()) { return -1; } Integer i; if (wait) { try { i = queue.take(); } catch (InterruptedException e) { throw new InterruptedIOException(); } checkInterrupt(); } else { i = queue.poll(); } if (i == null) { return -1; } return i; } @Override public int read() throws IOException { return read(true); } @Override public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int nb = 1; int i = read(true); if (i < 0) { return -1; } b[off++] = (byte) i; while (nb < len) { i = read(false); if (i < 0) { return nb; } b[off++] = (byte) i; nb++; } return nb; } @Override public int available() throws IOException { return queue.size(); } } private class Pipe implements Runnable { public void run() { try { while (running) { try { int c = in.read(); if (c == -1) { return; } else if (c == 4 && !getBoolean(IGNORE_INTERRUPTS)) { err.println("^D"); } else if (c == 3 && !getBoolean(IGNORE_INTERRUPTS)) { err.println("^C"); interrupt(); } queue.put(c); } catch (Throwable t) { return; } } } finally { eof = true; try { queue.put(-1); } catch (InterruptedException e) { } } } } }