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 */
019package org.crsh.shell.impl.command;
020
021import org.crsh.cli.impl.completion.CompletionMatch;
022import org.crsh.shell.impl.command.spi.CommandCreationException;
023import org.crsh.command.RuntimeContext;
024import org.crsh.shell.impl.command.spi.CommandInvoker;
025import org.crsh.shell.impl.command.spi.ShellCommand;
026import org.crsh.lang.script.ScriptRepl;
027import org.crsh.plugin.PluginContext;
028import org.crsh.repl.Repl;
029import org.crsh.repl.ReplSession;
030import org.crsh.shell.Shell;
031import org.crsh.shell.ShellProcess;
032import org.crsh.shell.ShellResponse;
033import org.crsh.repl.EvalResponse;
034import org.crsh.shell.impl.command.spi.CommandManager;
035
036import java.io.Closeable;
037import java.security.Principal;
038import java.util.HashMap;
039import java.util.Map;
040import java.util.logging.Level;
041import java.util.logging.Logger;
042
043public 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}