001/*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019
020package org.crsh.shell.impl.remoting;
021
022import org.crsh.cli.impl.completion.CompletionMatch;
023import org.crsh.shell.Shell;
024import org.crsh.shell.ShellResponse;
025import org.crsh.util.CloseableList;
026import org.crsh.util.Statement;
027
028import java.io.Closeable;
029import java.io.IOException;
030import java.io.InputStream;
031import java.io.ObjectInputStream;
032import java.io.ObjectOutputStream;
033import java.io.OutputStream;
034
035public class ClientAutomaton implements Runnable {
036
037  /** . */
038  final Shell shell;
039
040  /** . */
041  final ObjectOutputStream out;
042
043  /** . */
044  final ObjectInputStream in;
045
046  /** . */
047  ClientProcessContext current;
048
049  /** . */
050  final CloseableList listeners;
051
052  /** . */
053  Integer width;
054
055  /** . */
056  Integer height;
057
058  /** . */
059  long last;
060
061  public ClientAutomaton(ObjectOutputStream out, ObjectInputStream in, Shell shell) {
062    CloseableList listeners = new CloseableList();
063    listeners.add(in);
064    listeners.add(out);
065
066    //
067    this.in = in;
068    this.out = out;
069    this.shell = shell;
070    this.listeners = listeners;
071    this.width = null;
072    this.height = null;
073  }
074
075  public ClientAutomaton(InputStream in,OutputStream out, Shell shell) throws IOException {
076    this(new ObjectOutputStream(out), new ObjectInputStream(in), shell);
077  }
078
079  public ClientAutomaton addCloseListener(Closeable closeable) {
080    listeners.add(closeable);
081    return this;
082  }
083
084  public void run() {
085    try {
086      while (!listeners.isClosed()) {
087        ClientMessage msg = (ClientMessage)in.readObject();
088
089        //
090        if (msg instanceof ClientMessage.GetWelcome) {
091          String welcome = shell.getWelcome();
092          out.writeObject(new ServerMessage.Welcome(welcome));
093          out.flush();
094        } else if (msg instanceof ClientMessage.GetPrompt) {
095          String prompt = shell.getPrompt();
096          out.writeObject(new ServerMessage.Prompt(prompt));
097          out.flush();
098        } else if (msg instanceof ClientMessage.GetCompletion) {
099          String prefix = ((ClientMessage.GetCompletion)msg).prefix;
100          CompletionMatch completion = shell.complete(prefix);
101          out.writeObject(new ServerMessage.Completion(completion));
102          out.flush();
103        } else if (msg instanceof ClientMessage.SetSize) {
104          ClientMessage.SetSize setSize = (ClientMessage.SetSize)msg;
105          width = setSize.width;
106          height = setSize.height;
107          last = System.currentTimeMillis();
108        } else if (msg instanceof ClientMessage.Execute) {
109          ClientMessage.Execute execute = (ClientMessage.Execute)msg;
110          width = execute.width;
111          height = execute.height;
112          last = System.currentTimeMillis();
113          current = new ClientProcessContext(this, shell.createProcess(execute.line));
114          current.execute();
115        } else if (msg instanceof ClientMessage.Cancel) {
116          if (current != null) {
117
118            // For now we
119            // 1/ end the context
120            // 2/ cancel the process
121            // it is not the best strategy instead we should
122            // 1/ cancel the process
123            // 2/ wait a few milli seconds
124            // 3/ if it's not ended then we end it
125
126            final ClientProcessContext context = current;
127            Statement statements = new Statement() {
128              @Override
129              protected void run() throws Throwable {
130                context.end(ShellResponse.cancelled());
131              }
132            }.with(new Statement() {
133              @Override
134              protected void run() throws Throwable {
135                context.process.cancel();
136              }
137            });
138            statements.all();
139          }
140        } else if (msg instanceof ClientMessage.Close) {
141          close();
142        }
143      }
144    }
145    catch (Exception e) {
146      e.printStackTrace();
147      //
148    }
149    finally {
150      close();
151    }
152  }
153
154  void close() {
155    listeners.close();
156  }
157
158  public int getWidth() {
159    return width;
160  }
161
162  public int getHeight() {
163    return height;
164  }
165}