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
020package org.crsh.shell.impl.remoting;
021
022import org.crsh.shell.ErrorKind;
023import org.crsh.text.Screenable;
024import org.crsh.shell.ShellProcess;
025import org.crsh.shell.ShellProcessContext;
026import org.crsh.shell.ShellResponse;
027import org.crsh.text.Style;
028import org.crsh.util.Statement;
029
030import java.io.IOException;
031import java.util.ArrayList;
032
033class 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}