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.ErrorType;
023import org.crsh.shell.ShellProcess;
024import org.crsh.shell.ShellProcessContext;
025import org.crsh.shell.ShellResponse;
026import org.crsh.text.Chunk;
027import org.crsh.util.Statement;
028
029import java.io.IOException;
030import java.util.ArrayList;
031
032class 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}