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    }