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.shell.impl.command.spi.CommandCreationException;
023    import org.crsh.command.RuntimeContext;
024    import org.crsh.shell.impl.command.spi.CommandInvoker;
025    import org.crsh.shell.impl.command.spi.ShellCommand;
026    import org.crsh.lang.script.ScriptRepl;
027    import org.crsh.plugin.PluginContext;
028    import org.crsh.repl.Repl;
029    import org.crsh.repl.ReplSession;
030    import org.crsh.shell.Shell;
031    import org.crsh.shell.ShellProcess;
032    import org.crsh.shell.ShellResponse;
033    import org.crsh.repl.EvalResponse;
034    import org.crsh.shell.impl.command.spi.CommandManager;
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    public class CRaSHSession extends HashMap<String, Object> implements Shell, Closeable, RuntimeContext, ReplSession {
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 (CommandManager manager : crash.activeManagers.values()) {
072            manager.init(this);
073          }
074        }
075        finally {
076          setPreviousLoader(previous);
077        }
078      }
079    
080      /**
081       * Returns the current repl of this session.
082       *
083       * @return the current repl
084       */
085      public Repl getRepl() {
086        return repl;
087      }
088    
089      /**
090       * Set the current repl of this session.
091       *
092       * @param repl the new repl
093       * @throws NullPointerException if the repl is null
094       */
095      public void setRepl(Repl repl) throws NullPointerException {
096        if (repl == null) {
097          throw new NullPointerException("No null repl accepted");
098        }
099        this.repl = repl;
100      }
101    
102      public Iterable<String> getCommandNames() {
103        return crash.getCommandNames();
104      }
105    
106      public ShellCommand<?> getCommand(String name) throws CommandCreationException {
107        return crash.getCommand(name);
108      }
109    
110      public PluginContext getContext() {
111        return crash.context;
112      }
113    
114      public Map<String, Object> getSession() {
115        return this;
116      }
117    
118      public Map<String, Object> getAttributes() {
119        return crash.context.getAttributes();
120      }
121    
122      public void close() {
123        ClassLoader previous = setCRaSHLoader();
124        try {
125          for (CommandManager manager : crash.activeManagers.values()) {
126            manager.destroy(this);
127          }
128        }
129        finally {
130          setPreviousLoader(previous);
131        }
132      }
133    
134      // Shell implementation **********************************************************************************************
135    
136      public String getWelcome() {
137        ClassLoader previous = setCRaSHLoader();
138        try {
139          CommandManager groovy = crash.activeManagers.get("groovy");
140          if (groovy != null) {
141            return groovy.doCallBack(this, "welcome", "");
142          } else {
143            return "";
144          }
145        }
146        finally {
147          setPreviousLoader(previous);
148        }
149      }
150    
151      public String getPrompt() {
152        ClassLoader previous = setCRaSHLoader();
153        try {
154          CommandManager groovy = crash.activeManagers.get("groovy");
155          if (groovy != null) {
156            return groovy.doCallBack(this, "prompt", "% ");
157          } else {
158            return "% ";
159          }
160        }
161        finally {
162          setPreviousLoader(previous);
163        }
164      }
165    
166      public ShellProcess createProcess(String request) {
167        log.log(Level.FINE, "Invoking request " + request);
168        String trimmedRequest = request.trim();
169        final StringBuilder msg = new StringBuilder();
170        final ShellResponse response;
171        if ("bye".equals(trimmedRequest) || "exit".equals(trimmedRequest)) {
172          response = ShellResponse.close();
173        } else {
174          EvalResponse r = repl.eval(this, request);
175          if (r instanceof EvalResponse.Response) {
176            EvalResponse.Response rr = (EvalResponse.Response)r;
177            response = rr.response;
178          } else {
179            final CommandInvoker<Void, ?> pipeLine = ((EvalResponse.Invoke)r).invoker;
180            return new CRaSHCommandProcess(this, request, pipeLine);
181          }
182        }
183        return new CRaSHResponseProcess(this, request, msg, response);
184      }
185    
186      /**
187       * For now basic implementation
188       */
189      public CompletionMatch complete(final String prefix) {
190        ClassLoader previous = setCRaSHLoader();
191        try {
192          return repl.complete(this, prefix);
193        }
194        finally {
195          setPreviousLoader(previous);
196        }
197      }
198    
199      ClassLoader setCRaSHLoader() {
200        Thread thread = Thread.currentThread();
201        ClassLoader previous = thread.getContextClassLoader();
202        thread.setContextClassLoader(crash.context.getLoader());
203        return previous;
204      }
205    
206      void setPreviousLoader(ClassLoader previous) {
207        Thread.currentThread().setContextClassLoader(previous);
208      }
209    
210    }