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 write(Chunk chunk) throws IOException {
153        provide(chunk);
154      }
155    
156      public void provide(Chunk element) throws IOException {
157        if (!closed) {
158          buffer.add(element);
159        }
160      }
161    
162      public Class<Chunk> getConsumedType() {
163        return Chunk.class;
164      }
165    
166      public synchronized void flush() {
167        if (!closed) {
168          if (buffer.size() > 0) {
169            try {
170              for (Chunk chunk : buffer) {
171                client.out.writeObject(new ServerMessage.Chunk(chunk));
172              }
173              client.out.writeObject(new ServerMessage.Flush());
174              client.out.flush();
175            }
176            catch (IOException ignore) {
177              //
178            }
179            finally {
180              buffer.clear();
181            }
182          }
183        }
184      }
185    
186      public synchronized void end(ShellResponse response) {
187    
188        // It may have been cancelled concurrently
189        if (client.current == this) {
190    
191          // Flush what we have in buffer first
192          flush();
193    
194          // Send end message
195          try {
196            client.current = null;
197            client.out.writeObject(new ServerMessage.End(response));
198            client.out.flush();
199          }
200          catch (IOException ignore) {
201            //
202          }
203          finally {
204            closed = true;
205            if (response instanceof ShellResponse.Close) {
206              client.close();
207            }
208          }
209        }
210      }
211    }