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.cmdline.CommandCompletion;
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.text.Chunk;
028    import org.crsh.util.CloseableList;
029    import org.slf4j.Logger;
030    import org.slf4j.LoggerFactory;
031    
032    import java.io.Closeable;
033    import java.io.IOException;
034    import java.io.InputStream;
035    import java.io.ObjectInputStream;
036    import java.io.ObjectOutputStream;
037    import java.io.OutputStream;
038    import java.lang.reflect.UndeclaredThrowableException;
039    
040    public class ServerAutomaton implements Shell {
041    
042      /** . */
043      final Logger log = LoggerFactory.getLogger(ServerAutomaton.class);
044    
045      /** . */
046      final ObjectInputStream in;
047    
048      /** . */
049      final ObjectOutputStream out;
050    
051      /** . */
052      ServerProcess process;
053    
054      /** . */
055      final CloseableList listeners;
056    
057      public ServerAutomaton(ObjectOutputStream out, ObjectInputStream in) {
058        CloseableList listeners = new CloseableList();
059        listeners.add(in);
060        listeners.add(out);
061    
062        //
063        this.in = in;
064        this.out = out;
065        this.listeners = listeners;
066      }
067    
068      public ServerAutomaton(InputStream in, OutputStream out) throws IOException {
069        this(new ObjectOutputStream(out), new ObjectInputStream(in));
070      }
071    
072      public ServerAutomaton addCloseListener(Closeable closeable) {
073        listeners.add(closeable);
074        return this;
075      }
076    
077      public String getWelcome() {
078        try {
079          out.writeObject(ClientMessage.GET_WELCOME);
080          out.flush();
081          return (String)in.readObject();
082        }
083        catch (Exception e) {
084          throw new UndeclaredThrowableException(e);
085        }
086      }
087    
088      public String getPrompt() {
089        try {
090          out.writeObject(ClientMessage.GET_PROMPT);
091          out.flush();
092          return (String)in.readObject();
093        }
094        catch (Exception e) {
095          throw new UndeclaredThrowableException(e);
096        }
097      }
098    
099      public ShellProcess createProcess(String request) throws IllegalStateException {
100        return new ServerProcess(this, request);
101      }
102    
103      public CommandCompletion complete(String prefix) {
104        try {
105          out.writeObject(ClientMessage.GET_COMPLETION);
106          out.writeObject(prefix);
107          out.flush();
108          return (CommandCompletion)in.readObject();
109        }
110        catch (Exception e) {
111          throw new UndeclaredThrowableException(e);
112        }
113      }
114    
115      void close() {
116        listeners.close();
117      }
118    
119      void execute(ServerProcess process, ShellProcessContext processContext) throws IllegalStateException {
120    
121        if (this.process == null) {
122          this.process = process;
123        } else {
124          throw new IllegalStateException();
125        }
126    
127        //
128        ShellResponse response = null;
129        try {
130          out.writeObject(ClientMessage.EXECUTE);
131          out.writeObject(processContext.getWidth());
132          out.writeObject(processContext.getHeight());
133          out.writeObject(process.line);
134          out.flush();
135    
136          //
137          while (response == null) {
138            ServerMessage msg = (ServerMessage)in.readObject();
139            switch (msg) {
140              case GET_WIDTH:
141                int width = processContext.getWidth();
142                out.writeObject(width);
143                out.flush();
144                break;
145              case GET_HEIGHT:
146                int height = processContext.getHeight();
147                out.writeObject(height);
148                out.flush();
149                break;
150              case READLINE:
151                String request = (String)in.readObject();
152                boolean echo = (Boolean)in.readObject();
153                String line = processContext.readLine(request, echo);
154                out.writeObject(line);
155                out.flush();
156                break;
157              case END:
158                response = (ShellResponse)in.readObject();
159                break;
160              case CHUNK:
161                Chunk chunk = (Chunk)in.readObject();
162                processContext.provide(chunk);
163                break;
164              case FLUSH:
165                processContext.flush();
166                break;
167              default:
168                response = ShellResponse.internalError("Unexpected");
169                break;
170            }
171          }
172        }
173        catch (Exception e) {
174          log.error("Remoting issue", e);
175          response = ShellResponse.internalError("Remoting issue", e);
176        }
177        finally {
178    
179          //
180          this.process = null;
181    
182          //
183          if (response != null) {
184            processContext.end(response);
185          } else {
186            processContext.end(ShellResponse.internalError(""));
187          }
188        }
189      }
190    
191      void cancel(ServerProcess process) throws IllegalStateException {
192        if (process == this.process) {
193          this.process = null;
194          try {
195            out.writeObject(ClientMessage.CANCEL);
196            out.flush();
197          }
198          catch (IOException ignore) {
199          }
200        }
201      }
202    }