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    package org.crsh.shell.impl.command;
020    
021    import org.crsh.cli.impl.completion.CompletionMatch;
022    import org.crsh.lang.spi.Compiler;
023    import org.crsh.lang.spi.Language;
024    import org.crsh.lang.spi.Repl;
025    import org.crsh.lang.spi.ReplResponse;
026    import org.crsh.shell.impl.command.spi.CommandException;
027    import org.crsh.command.RuntimeContext;
028    import org.crsh.shell.impl.command.spi.CommandInvoker;
029    import org.crsh.shell.impl.command.spi.Command;
030    import org.crsh.lang.impl.script.ScriptRepl;
031    import org.crsh.plugin.PluginContext;
032    import org.crsh.shell.Shell;
033    import org.crsh.shell.ShellProcess;
034    import org.crsh.shell.ShellResponse;
035    
036    import java.io.Closeable;
037    import java.security.Principal;
038    import java.util.HashMap;
039    import java.util.Map;
040    import java.util.logging.Level;
041    import java.util.logging.Logger;
042    
043    class CRaSHSession extends HashMap<String, Object> implements Shell, Closeable, RuntimeContext, ShellSession {
044    
045      /** . */
046      static final Logger log = Logger.getLogger(CRaSHSession.class.getName());
047    
048      /** . */
049      static final Logger accessLog = Logger.getLogger("org.crsh.shell.access");
050    
051      /** . */
052      public final CRaSH crash;
053    
054      /** . */
055      final Principal user;
056    
057      /** . */
058      private Repl repl = ScriptRepl.getInstance();
059    
060      CRaSHSession(final CRaSH crash, Principal user) {
061        // Set variable available to all scripts
062        put("crash", crash);
063    
064        //
065        this.crash = crash;
066        this.user = user;
067    
068        //
069        ClassLoader previous = setCRaSHLoader();
070        try {
071          for (Language manager : crash.langs) {
072            manager.init(this);
073          }
074        }
075        finally {
076          setPreviousLoader(previous);
077        }
078      }
079    
080      public Repl getRepl() {
081        return repl;
082      }
083    
084      public void setRepl(Repl repl) throws NullPointerException {
085        if (repl == null) {
086          throw new NullPointerException("No null repl accepted");
087        }
088        this.repl = repl;
089      }
090    
091      public Iterable<Map.Entry<String, String>> getCommands() {
092        return crash.getCommands();
093      }
094    
095      public Command<?> getCommand(String name) throws CommandException {
096        return crash.getCommand(name);
097      }
098    
099      public PluginContext getContext() {
100        return crash.context;
101      }
102    
103      public Map<String, Object> getSession() {
104        return this;
105      }
106    
107      public Map<String, Object> getAttributes() {
108        return crash.context.getAttributes();
109      }
110    
111      public void close() {
112        ClassLoader previous = setCRaSHLoader();
113        try {
114          for (Language manager : crash.langs) {
115            manager.destroy(this);
116          }
117        }
118        finally {
119          setPreviousLoader(previous);
120        }
121      }
122    
123      // Shell implementation **********************************************************************************************
124    
125      public String getWelcome() {
126        ClassLoader previous = setCRaSHLoader();
127        try {
128          Compiler groovy = crash.scriptResolver.getCompiler("groovy");
129          if (groovy != null) {
130            return groovy.doCallBack(this, "welcome", "");
131          } else {
132            return "";
133          }
134        }
135        finally {
136          setPreviousLoader(previous);
137        }
138      }
139    
140      public String getPrompt() {
141        ClassLoader previous = setCRaSHLoader();
142        try {
143          Compiler groovy = crash.scriptResolver.getCompiler("groovy");
144          if (groovy != null) {
145            return groovy.doCallBack(this, "prompt", "% ");
146          } else {
147            return "% ";
148          }
149        }
150        finally {
151          setPreviousLoader(previous);
152        }
153      }
154    
155      public ShellProcess createProcess(String request) {
156        log.log(Level.FINE, "Invoking request " + request);
157        String trimmedRequest = request.trim();
158        final StringBuilder msg = new StringBuilder();
159        final ShellResponse response;
160        if ("bye".equals(trimmedRequest) || "exit".equals(trimmedRequest)) {
161          response = ShellResponse.close();
162        } else {
163          ReplResponse r = repl.eval(this, request);
164          if (r instanceof ReplResponse.Response) {
165            ReplResponse.Response rr = (ReplResponse.Response)r;
166            response = rr.response;
167          } else {
168            final CommandInvoker<Void, ?> pipeLine = ((ReplResponse.Invoke)r).invoker;
169            return new CRaSHCommandProcess(this, request, pipeLine);
170          }
171        }
172        return new CRaSHResponseProcess(this, request, msg, response);
173      }
174    
175      /**
176       * For now basic implementation
177       */
178      public CompletionMatch complete(final String prefix) {
179        ClassLoader previous = setCRaSHLoader();
180        try {
181          return repl.complete(this, prefix);
182        }
183        finally {
184          setPreviousLoader(previous);
185        }
186      }
187    
188      ClassLoader setCRaSHLoader() {
189        Thread thread = Thread.currentThread();
190        ClassLoader previous = thread.getContextClassLoader();
191        thread.setContextClassLoader(crash.context.getLoader());
192        return previous;
193      }
194    
195      void setPreviousLoader(ClassLoader previous) {
196        Thread.currentThread().setContextClassLoader(previous);
197      }
198    
199    }