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 readLine(String msg, boolean echo) {
105          return caller.readLine(msg, echo);
106        }
107    
108        public void end(ShellResponse response) {
109    
110          synchronized (lock) {
111    
112            // Signal response
113            if (status == Status.CANCELED) {
114              caller.end(new ShellResponse.Cancelled());
115            } else {
116              caller.end(response);
117            }
118    
119            // Update state
120            current = null;
121            status = Status.AVAILABLE;
122          }
123        }
124    
125        // ShellProcess implementation *************************************************************************************
126    
127        public void execute(ShellProcessContext processContext) {
128          synchronized (lock) {
129    
130            if (status != Status.AVAILABLE) {
131              throw new IllegalStateException("State was " + status);
132            }
133    
134            //
135            // Update state
136            status = Status.EVALUATING;
137            current = this;
138            callee = shell.createProcess(request);
139            caller = processContext;
140          }
141    
142          //
143          executor.submit(current);
144        }
145    
146        public void cancel() {
147          synchronized (lock) {
148            if (status == Status.EVALUATING) {
149              status = Status.CANCELED;
150              callee.cancel();
151            }
152          }
153        }
154    
155        // Runnable implementation *****************************************************************************************
156    
157        public void run() {
158          callee.execute(this);
159        }
160      }
161    
162      // Shell implementation **********************************************************************************************
163    
164      public String getWelcome() {
165        return shell.getWelcome();
166      }
167    
168      public String getPrompt() {
169        return shell.getPrompt();
170      }
171    
172      public Map<String, String> complete(String prefix) {
173        return shell.complete(prefix);
174      }
175    
176      public ShellProcess createProcess(String request) {
177        return new Process(request);
178      }
179    }