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.lang.spi.Compiler;
023import org.crsh.lang.spi.Language;
024import org.crsh.lang.spi.Repl;
025import org.crsh.lang.spi.ReplResponse;
026import org.crsh.shell.impl.command.spi.CommandException;
027import org.crsh.command.RuntimeContext;
028import org.crsh.shell.impl.command.spi.CommandInvoker;
029import org.crsh.shell.impl.command.spi.Command;
030import org.crsh.lang.impl.script.ScriptRepl;
031import org.crsh.plugin.PluginContext;
032import org.crsh.shell.Shell;
033import org.crsh.shell.ShellProcess;
034import org.crsh.shell.ShellResponse;
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
043class 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}