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.shell.ErrorKind;
023    import org.crsh.text.Screenable;
024    import org.crsh.shell.ShellProcess;
025    import org.crsh.shell.ShellProcessContext;
026    import org.crsh.shell.ShellResponse;
027    import org.crsh.text.Style;
028    import org.crsh.util.Statement;
029    
030    import java.io.IOException;
031    import java.util.ArrayList;
032    
033    class ClientProcessContext implements ShellProcessContext {
034    
035      /** . */
036      final ClientAutomaton client;
037    
038      /** . */
039      final ShellProcess process;
040    
041      /** . */
042      final ArrayList<ServerMessage.Chunk> buffer;
043    
044      /** . */
045      private boolean closed;
046    
047      ClientProcessContext(ClientAutomaton client, ShellProcess process) {
048        this.client = client;
049        this.process = process;
050        this.buffer = new ArrayList<ServerMessage.Chunk>(1000);
051        this.closed = false;
052      }
053    
054      /**
055       * Ensure we have a recent size, the size is considered as recent if it's younger than 2 second, otherwise
056       * send a get size message.
057       */
058      private void ensureSize() {
059        if (System.currentTimeMillis() - client.last > 2000) {
060          synchronized (this) {
061            try {
062              client.out.writeObject(new ServerMessage.GetSize());
063              client.out.flush();
064            }
065            catch (Exception e) {
066              //
067            }
068          }
069        }
070      }
071    
072      void execute() {
073        try {
074          process.execute(this);
075        }
076        catch(final Throwable t) {
077          new Statement() {
078            @Override
079            protected void run() throws Throwable {
080              // If it's not executing then we attempt to end it
081              end(ShellResponse.error(ErrorKind.INTERNAL, "Unexpected process execution error", t));
082            }
083          }.all();
084        }
085      }
086    
087      public int getWidth() {
088        if (!closed) {
089          ensureSize();
090          return client.getWidth();
091        } else {
092          return -1;
093        }
094      }
095    
096      public int getHeight() {
097        if (!closed) {
098          ensureSize();
099          return client.getHeight();
100        } else {
101          return -1;
102        }
103      }
104    
105      public boolean takeAlternateBuffer() {
106        if (!closed) {
107          try {
108            client.out.writeObject(new ServerMessage.UseAlternateBuffer());
109            client.out.flush();
110          }
111          catch (Exception e) {
112            //
113          }
114        }
115    
116        // For now we suppose any impl return true;
117        return true;
118      }
119    
120      public boolean releaseAlternateBuffer() {
121        if (!closed) {
122          try {
123            client.out.writeObject(new ServerMessage.UseMainBuffer());
124            client.out.flush();
125          }
126          catch (Exception e) {
127            //
128          }
129        }
130    
131        // For now we suppose any impl return true;
132        return true;
133      }
134    
135      public String getProperty(String name) {
136        return null;
137      }
138    
139      public String readLine(String msg, boolean echo) {
140    //    try {
141    //      client.out.writeObject(ServerMessage.READLINE);
142    //      client.out.writeObject(msg);
143    //      client.out.writeObject(echo);
144    //      client.out.flush();
145    //      return (String)client.in.readObject();
146    //    }
147    //    catch (Exception e) {
148    //      return null;
149    //    }
150        return null;
151      }
152    
153      @Override
154      public Screenable append(CharSequence s) throws IOException {
155        if (!closed) {
156          buffer.add(new ServerMessage.Chunk.Text(s));
157        }
158        return this;
159      }
160    
161      @Override
162      public Screenable append(char c) throws IOException {
163        return append(Character.toString(c));
164      }
165    
166      @Override
167      public Screenable append(CharSequence csq, int start, int end) throws IOException {
168        return append(csq.subSequence(start, end));
169      }
170    
171      @Override
172      public Screenable append(Style style) throws IOException {
173        if (!closed) {
174          buffer.add(new ServerMessage.Chunk.Style(style));
175        }
176        return this;
177      }
178    
179      @Override
180      public Screenable cls() throws IOException {
181        if (!closed) {
182          buffer.add(new ServerMessage.Chunk.Cls());
183        }
184        return this;
185      }
186    
187      public synchronized void flush() {
188        if (!closed) {
189          if (buffer.size() > 0) {
190            try {
191              for (ServerMessage.Chunk chunk : buffer) {
192                client.out.writeObject(chunk);
193              }
194              client.out.writeObject(new ServerMessage.Flush());
195              client.out.flush();
196            }
197            catch (IOException ignore) {
198              //
199            }
200            finally {
201              buffer.clear();
202            }
203          }
204        }
205      }
206    
207      public synchronized void end(ShellResponse response) {
208    
209        // It may have been cancelled concurrently
210        if (client.current == this) {
211    
212          // Flush what we have in buffer first
213          flush();
214    
215          // Send end message
216          try {
217            client.current = null;
218            client.out.writeObject(new ServerMessage.End(response));
219            client.out.flush();
220          }
221          catch (IOException ignore) {
222            //
223          }
224          finally {
225            closed = true;
226            if (response instanceof ShellResponse.Close) {
227              client.close();
228            }
229          }
230        }
231      }
232    }