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.cmdline.CommandCompletion;
023 import org.crsh.shell.Shell;
024 import org.crsh.shell.ShellProcess;
025 import org.crsh.shell.ShellProcessContext;
026 import org.crsh.shell.ShellResponse;
027 import org.crsh.text.Chunk;
028 import org.crsh.util.CloseableList;
029 import org.slf4j.Logger;
030 import org.slf4j.LoggerFactory;
031
032 import java.io.Closeable;
033 import java.io.IOException;
034 import java.io.InputStream;
035 import java.io.ObjectInputStream;
036 import java.io.ObjectOutputStream;
037 import java.io.OutputStream;
038 import java.lang.reflect.UndeclaredThrowableException;
039
040 public class ServerAutomaton implements Shell {
041
042 /** . */
043 final Logger log = LoggerFactory.getLogger(ServerAutomaton.class);
044
045 /** . */
046 final ObjectInputStream in;
047
048 /** . */
049 final ObjectOutputStream out;
050
051 /** . */
052 ServerProcess process;
053
054 /** . */
055 final CloseableList listeners;
056
057 public ServerAutomaton(ObjectOutputStream out, ObjectInputStream in) {
058 CloseableList listeners = new CloseableList();
059 listeners.add(in);
060 listeners.add(out);
061
062 //
063 this.in = in;
064 this.out = out;
065 this.listeners = listeners;
066 }
067
068 public ServerAutomaton(InputStream in, OutputStream out) throws IOException {
069 this(new ObjectOutputStream(out), new ObjectInputStream(in));
070 }
071
072 public ServerAutomaton addCloseListener(Closeable closeable) {
073 listeners.add(closeable);
074 return this;
075 }
076
077 public String getWelcome() {
078 try {
079 out.writeObject(ClientMessage.GET_WELCOME);
080 out.flush();
081 return (String)in.readObject();
082 }
083 catch (Exception e) {
084 throw new UndeclaredThrowableException(e);
085 }
086 }
087
088 public String getPrompt() {
089 try {
090 out.writeObject(ClientMessage.GET_PROMPT);
091 out.flush();
092 return (String)in.readObject();
093 }
094 catch (Exception e) {
095 throw new UndeclaredThrowableException(e);
096 }
097 }
098
099 public ShellProcess createProcess(String request) throws IllegalStateException {
100 return new ServerProcess(this, request);
101 }
102
103 public CommandCompletion complete(String prefix) {
104 try {
105 out.writeObject(ClientMessage.GET_COMPLETION);
106 out.writeObject(prefix);
107 out.flush();
108 return (CommandCompletion)in.readObject();
109 }
110 catch (Exception e) {
111 throw new UndeclaredThrowableException(e);
112 }
113 }
114
115 void close() {
116 listeners.close();
117 }
118
119 void execute(ServerProcess process, ShellProcessContext processContext) throws IllegalStateException {
120
121 if (this.process == null) {
122 this.process = process;
123 } else {
124 throw new IllegalStateException();
125 }
126
127 //
128 ShellResponse response = null;
129 try {
130 out.writeObject(ClientMessage.EXECUTE);
131 out.writeObject(processContext.getWidth());
132 out.writeObject(processContext.getHeight());
133 out.writeObject(process.line);
134 out.flush();
135
136 //
137 while (response == null) {
138 ServerMessage msg = (ServerMessage)in.readObject();
139 switch (msg) {
140 case GET_SIZE:
141 out.writeObject(ClientMessage.SET_SIZE);
142 int width = processContext.getWidth();
143 int height = processContext.getHeight();
144 out.writeObject(width);
145 out.writeObject(height);
146 out.flush();
147 break;
148 case READLINE:
149 // // This case should not really well supported ?
150 // String request = (String)in.readObject();
151 // boolean echo = (Boolean)in.readObject();
152 // String line = processContext.readLine(request, echo);
153 // out.writeObject(line);
154 // out.flush();
155 // break;
156 throw new UnsupportedOperationException("Not handled");
157 case END:
158 response = (ShellResponse)in.readObject();
159 break;
160 case CHUNK:
161 Chunk chunk = (Chunk)in.readObject();
162 processContext.provide(chunk);
163 break;
164 case FLUSH:
165 processContext.flush();
166 break;
167 default:
168 response = ShellResponse.internalError("Unexpected");
169 break;
170 }
171 }
172 }
173 catch (Exception e) {
174 log.error("Remoting issue", e);
175 response = ShellResponse.internalError("Remoting issue", e);
176 }
177 finally {
178
179 //
180 this.process = null;
181
182 //
183 if (response != null) {
184 processContext.end(response);
185 } else {
186 processContext.end(ShellResponse.internalError(""));
187 }
188 }
189 }
190
191 void cancel(ServerProcess process) throws IllegalStateException {
192 if (process == this.process) {
193 this.process = null;
194 try {
195 out.writeObject(ClientMessage.CANCEL);
196 out.flush();
197 }
198 catch (IOException ignore) {
199 }
200 }
201 }
202 }