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