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.ShellProcess;
025import org.crsh.shell.ShellProcessContext;
026import org.crsh.shell.ShellResponse;
027import org.crsh.util.CloseableList;
028
029import java.io.Closeable;
030import java.io.IOException;
031import java.io.InputStream;
032import java.io.ObjectInputStream;
033import java.io.ObjectOutputStream;
034import java.io.OutputStream;
035import java.lang.reflect.UndeclaredThrowableException;
036import java.util.logging.Level;
037import java.util.logging.Logger;
038
039public class ServerAutomaton implements Shell {
040
041  /** . */
042  final Logger log = Logger.getLogger(ServerAutomaton.class.getName());
043
044  /** . */
045  final ObjectInputStream in;
046
047  /** . */
048  final ObjectOutputStream out;
049
050  /** . */
051  ServerProcess process;
052
053  /** . */
054  final CloseableList listeners;
055
056  public ServerAutomaton(ObjectOutputStream out, ObjectInputStream in) {
057    CloseableList listeners = new CloseableList();
058    listeners.add(in);
059    listeners.add(out);
060
061    //
062    this.in = in;
063    this.out = out;
064    this.listeners = listeners;
065  }
066
067  public ServerAutomaton(InputStream in, OutputStream out) throws IOException {
068    this(new ObjectOutputStream(out), new ObjectInputStream(in));
069  }
070
071  public ServerAutomaton addCloseListener(Closeable closeable) {
072    listeners.add(closeable);
073    return this;
074  }
075
076  public String getWelcome() {
077    try {
078      out.writeObject(new ClientMessage.GetWelcome());
079      out.flush();
080      return ((ServerMessage.Welcome)in.readObject()).value;
081    }
082    catch (Exception e) {
083      throw new UndeclaredThrowableException(e);
084    }
085  }
086
087  public String getPrompt() {
088    try {
089      out.writeObject(new ClientMessage.GetPrompt());
090      out.flush();
091      return ((ServerMessage.Prompt)in.readObject()).value;
092    }
093    catch (Exception e) {
094      throw new UndeclaredThrowableException(e);
095    }
096  }
097
098  public ShellProcess createProcess(String request) throws IllegalStateException {
099    return new ServerProcess(this, request);
100  }
101
102  public CompletionMatch complete(String prefix) {
103    try {
104      out.writeObject(new ClientMessage.GetCompletion(prefix));
105      out.flush();
106      return ((ServerMessage.Completion)in.readObject()).value;
107    }
108    catch (Exception e) {
109      throw new UndeclaredThrowableException(e);
110    }
111  }
112
113  public void close() {
114    listeners.close();
115  }
116
117  void execute(ServerProcess process, ShellProcessContext processContext) throws IllegalStateException {
118
119    if (this.process == null) {
120      this.process = process;
121    } else {
122      throw new IllegalStateException();
123    }
124
125    //
126    ShellResponse response = null;
127    try {
128      out.writeObject(new ClientMessage.Execute(processContext.getWidth(), processContext.getHeight(), process.line));
129      out.flush();
130
131      //
132      while (response == null) {
133        ServerMessage msg = (ServerMessage)in.readObject();
134        if (msg instanceof ServerMessage.GetSize) {
135          out.writeObject(new ClientMessage.SetSize(processContext.getWidth(), processContext.getHeight()));
136          out.flush();
137        } else if (msg instanceof ServerMessage.ReadLine) {
138//            // This case should not really well supported ?
139//            String request = (String)in.readObject();
140//            boolean echo = (Boolean)in.readObject();
141//            String line = processContext.readLine(request, echo);
142//            out.writeObject(line);
143//            out.flush();
144//            break;
145          throw new UnsupportedOperationException("Not handled");
146        } else if (msg instanceof ServerMessage.UseAlternateBuffer) {
147          processContext.takeAlternateBuffer();
148        } else if (msg instanceof ServerMessage.UseMainBuffer) {
149          processContext.releaseAlternateBuffer();
150        } else if (msg instanceof ServerMessage.End) {
151          response = ((ServerMessage.End)msg).response;
152        } else if (msg instanceof ServerMessage.Chunk) {
153
154          ServerMessage.Chunk chunk = ((ServerMessage.Chunk)msg);
155          if (chunk instanceof ServerMessage.Chunk.Text) {
156            processContext.append(((ServerMessage.Chunk.Text)chunk).payload);
157          } else if (chunk instanceof ServerMessage.Chunk.Cls) {
158            processContext.cls();
159          } else {
160            processContext.append(((ServerMessage.Chunk.Style)chunk).payload);
161          }
162        } else if (msg instanceof ServerMessage.Flush) {
163          processContext.flush();
164        } else {
165          response = ShellResponse.internalError("Unexpected");
166        }
167      }
168    }
169    catch (Exception e) {
170      log.log(Level.SEVERE, "Remoting issue", e);
171      response = ShellResponse.internalError("Remoting issue", e);
172    }
173    finally {
174
175      //
176      this.process = null;
177
178      //
179      if (response != null) {
180        processContext.end(response);
181      } else {
182        processContext.end(ShellResponse.internalError(""));
183      }
184    }
185  }
186
187  void cancel(ServerProcess process) throws IllegalStateException {
188    if (process == this.process) {
189      this.process = null;
190      try {
191        out.writeObject(new ClientMessage.Cancel());
192        out.flush();
193      }
194      catch (IOException ignore) {
195      }
196    }
197  }
198}