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.cli.impl.completion.CompletionMatch;
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.util.CloseableList;
028
029 import java.io.Closeable;
030 import java.io.IOException;
031 import java.io.InputStream;
032 import java.io.ObjectInputStream;
033 import java.io.ObjectOutputStream;
034 import java.io.OutputStream;
035 import java.lang.reflect.UndeclaredThrowableException;
036 import java.util.logging.Level;
037 import java.util.logging.Logger;
038
039 public class ServerAutomaton implements Shell {
040
041 /** . */
042 final Logger log = Logger.getLogger(ServerAutomaton.class.getName());
043
044 /** . */
045 final ObjectInputStream in;
046
047 /** . */
048 final ObjectOutputStream out;
049
050 /** . */
051 ServerProcess process;
052
053 /** . */
054 final CloseableList listeners;
055
056 public ServerAutomaton(ObjectOutputStream out, ObjectInputStream in) {
057 CloseableList listeners = new CloseableList();
058 listeners.add(in);
059 listeners.add(out);
060
061 //
062 this.in = in;
063 this.out = out;
064 this.listeners = listeners;
065 }
066
067 public ServerAutomaton(InputStream in, OutputStream out) throws IOException {
068 this(new ObjectOutputStream(out), new ObjectInputStream(in));
069 }
070
071 public ServerAutomaton addCloseListener(Closeable closeable) {
072 listeners.add(closeable);
073 return this;
074 }
075
076 public String getWelcome() {
077 try {
078 out.writeObject(new ClientMessage.GetWelcome());
079 out.flush();
080 return ((ServerMessage.Welcome)in.readObject()).value;
081 }
082 catch (Exception e) {
083 throw new UndeclaredThrowableException(e);
084 }
085 }
086
087 public String getPrompt() {
088 try {
089 out.writeObject(new ClientMessage.GetPrompt());
090 out.flush();
091 return ((ServerMessage.Prompt)in.readObject()).value;
092 }
093 catch (Exception e) {
094 throw new UndeclaredThrowableException(e);
095 }
096 }
097
098 public ShellProcess createProcess(String request) throws IllegalStateException {
099 return new ServerProcess(this, request);
100 }
101
102 public CompletionMatch complete(String prefix) {
103 try {
104 out.writeObject(new ClientMessage.GetCompletion(prefix));
105 out.flush();
106 return ((ServerMessage.Completion)in.readObject()).value;
107 }
108 catch (Exception e) {
109 throw new UndeclaredThrowableException(e);
110 }
111 }
112
113 public void close() {
114 listeners.close();
115 }
116
117 void execute(ServerProcess process, ShellProcessContext processContext) throws IllegalStateException {
118
119 if (this.process == null) {
120 this.process = process;
121 } else {
122 throw new IllegalStateException();
123 }
124
125 //
126 ShellResponse response = null;
127 try {
128 out.writeObject(new ClientMessage.Execute(processContext.getWidth(), processContext.getHeight(), process.line));
129 out.flush();
130
131 //
132 while (response == null) {
133 ServerMessage msg = (ServerMessage)in.readObject();
134 if (msg instanceof ServerMessage.GetSize) {
135 out.writeObject(new ClientMessage.SetSize(processContext.getWidth(), processContext.getHeight()));
136 out.flush();
137 } else if (msg instanceof ServerMessage.ReadLine) {
138 // // This case should not really well supported ?
139 // String request = (String)in.readObject();
140 // boolean echo = (Boolean)in.readObject();
141 // String line = processContext.readLine(request, echo);
142 // out.writeObject(line);
143 // out.flush();
144 // break;
145 throw new UnsupportedOperationException("Not handled");
146 } else if (msg instanceof ServerMessage.UseAlternateBuffer) {
147 processContext.takeAlternateBuffer();
148 } else if (msg instanceof ServerMessage.UseMainBuffer) {
149 processContext.releaseAlternateBuffer();
150 } else if (msg instanceof ServerMessage.End) {
151 response = ((ServerMessage.End)msg).response;
152 } else if (msg instanceof ServerMessage.Chunk) {
153 processContext.provide(((ServerMessage.Chunk)msg).payload);
154 } else if (msg instanceof ServerMessage.Flush) {
155 processContext.flush();
156 } else {
157 response = ShellResponse.internalError("Unexpected");
158 }
159 }
160 }
161 catch (Exception e) {
162 log.log(Level.SEVERE, "Remoting issue", e);
163 response = ShellResponse.internalError("Remoting issue", e);
164 }
165 finally {
166
167 //
168 this.process = null;
169
170 //
171 if (response != null) {
172 processContext.end(response);
173 } else {
174 processContext.end(ShellResponse.internalError(""));
175 }
176 }
177 }
178
179 void cancel(ServerProcess process) throws IllegalStateException {
180 if (process == this.process) {
181 this.process = null;
182 try {
183 out.writeObject(new ClientMessage.Cancel());
184 out.flush();
185 }
186 catch (IOException ignore) {
187 }
188 }
189 }
190 }