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    
020    package org.crsh.shell.impl.remoting;
021    
022    import org.crsh.cli.impl.completion.CompletionMatch;
023    import org.crsh.shell.Shell;
024    import org.crsh.shell.ShellProcess;
025    import org.crsh.shell.ShellProcessContext;
026    import org.crsh.shell.ShellResponse;
027    import org.crsh.util.CloseableList;
028    
029    import java.io.Closeable;
030    import java.io.IOException;
031    import java.io.InputStream;
032    import java.io.ObjectInputStream;
033    import java.io.ObjectOutputStream;
034    import java.io.OutputStream;
035    import java.lang.reflect.UndeclaredThrowableException;
036    import java.util.logging.Level;
037    import java.util.logging.Logger;
038    
039    public 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              processContext.provide(((ServerMessage.Chunk)msg).payload);
154            } else if (msg instanceof ServerMessage.Flush) {
155              processContext.flush();
156            } else {
157              response = ShellResponse.internalError("Unexpected");
158            }
159          }
160        }
161        catch (Exception e) {
162          log.log(Level.SEVERE, "Remoting issue", e);
163          response = ShellResponse.internalError("Remoting issue", e);
164        }
165        finally {
166    
167          //
168          this.process = null;
169    
170          //
171          if (response != null) {
172            processContext.end(response);
173          } else {
174            processContext.end(ShellResponse.internalError(""));
175          }
176        }
177      }
178    
179      void cancel(ServerProcess process) throws IllegalStateException {
180        if (process == this.process) {
181          this.process = null;
182          try {
183            out.writeObject(new ClientMessage.Cancel());
184            out.flush();
185          }
186          catch (IOException ignore) {
187          }
188        }
189      }
190    }