001 /*
002 * Copyright (C) 2010 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.concurrent;
021
022 import org.crsh.shell.Shell;
023 import org.crsh.shell.ShellProcess;
024 import org.crsh.shell.ShellResponse;
025 import org.crsh.shell.ShellProcessContext;
026
027 import java.util.Map;
028 import java.util.concurrent.ExecutorService;
029
030 /**
031 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
032 * @version $Revision$
033 */
034 public class AsyncShell implements Shell {
035
036 /** . */
037 private final Object lock;
038
039 /** . */
040 private Status status;
041
042 /** . */
043 private Shell shell;
044
045 /** . */
046 private Process current;
047
048 /** . */
049 private final ExecutorService executor;
050
051 public AsyncShell(ExecutorService executor, Shell shell) {
052 this.lock = new Object();
053 this.status = Status.AVAILABLE;
054 this.shell = shell;
055 this.current = null;
056 this.executor = executor;
057 }
058
059 public Status getStatus() {
060 synchronized (lock) {
061 return status;
062 }
063 }
064
065 public void close() {
066 synchronized (lock) {
067 switch (status) {
068 case INITIAL:
069 case AVAILABLE:
070 break;
071 case CANCELED:
072 case EVALUATING:
073 throw new UnsupportedOperationException("todo :-) " + status);
074 case CLOSED:
075 break;
076 }
077 status = Status.CLOSED;
078 }
079 }
080
081 private class Process implements ShellProcessContext, Runnable, ShellProcess {
082
083
084 /** . */
085 private final String request;
086
087 /** . */
088 private ShellProcessContext caller;
089
090 /** . */
091 private ShellProcess callee;
092
093 private Process(String request) {
094 this.request = request;
095 this.callee = null;
096 }
097
098 // ShellProcessContext implementation ******************************************************************************
099
100 public int getWidth() {
101 return caller.getWidth();
102 }
103
104 public String getProperty(String name) {
105 return caller.getProperty(name);
106 }
107
108 public String readLine(String msg, boolean echo) {
109 return caller.readLine(msg, echo);
110 }
111
112 public void end(ShellResponse response) {
113
114 synchronized (lock) {
115
116 // Signal response
117 if (status == Status.CANCELED) {
118 caller.end(new ShellResponse.Cancelled());
119 } else {
120 caller.end(response);
121 }
122
123 // Update state
124 current = null;
125 status = Status.AVAILABLE;
126 }
127 }
128
129 // ShellProcess implementation *************************************************************************************
130
131 public void execute(ShellProcessContext processContext) {
132 synchronized (lock) {
133
134 if (status != Status.AVAILABLE) {
135 throw new IllegalStateException("State was " + status);
136 }
137
138 //
139 // Update state
140 status = Status.EVALUATING;
141 current = this;
142 callee = shell.createProcess(request);
143 caller = processContext;
144 }
145
146 //
147 executor.submit(current);
148 }
149
150 public void cancel() {
151 synchronized (lock) {
152 if (status == Status.EVALUATING) {
153 status = Status.CANCELED;
154 callee.cancel();
155 }
156 }
157 }
158
159 // Runnable implementation *****************************************************************************************
160
161 public void run() {
162 callee.execute(this);
163 }
164 }
165
166 // Shell implementation **********************************************************************************************
167
168 public String getWelcome() {
169 return shell.getWelcome();
170 }
171
172 public String getPrompt() {
173 return shell.getPrompt();
174 }
175
176 public Map<String, String> complete(String prefix) {
177 return shell.complete(prefix);
178 }
179
180 public ShellProcess createProcess(String request) {
181 return new Process(request);
182 }
183 }