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.ShellResponse;
025    import org.crsh.util.CloseableList;
026    import org.crsh.util.Statement;
027    
028    import java.io.Closeable;
029    import java.io.IOException;
030    import java.io.InputStream;
031    import java.io.ObjectInputStream;
032    import java.io.ObjectOutputStream;
033    import java.io.OutputStream;
034    
035    public 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    }