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 }