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
020 package org.crsh.shell.impl.command;
021
022 import org.crsh.cli.descriptor.Format;
023 import org.crsh.command.BaseCommand;
024 import org.crsh.lang.java.ShellCommandImpl;
025 import org.crsh.shell.impl.command.spi.CommandCreationException;
026 import org.crsh.shell.impl.command.spi.ShellCommand;
027 import org.crsh.plugin.PluginContext;
028 import org.crsh.plugin.ResourceKind;
029 import org.crsh.shell.impl.command.spi.CommandManager;
030 import org.crsh.shell.impl.command.spi.CommandResolution;
031 import org.crsh.shell.impl.command.system.help;
032 import org.crsh.shell.impl.command.system.repl;
033 import org.crsh.util.TimestampedObject;
034 import org.crsh.vfs.Resource;
035
036 import java.security.Principal;
037 import java.util.HashMap;
038 import java.util.LinkedHashSet;
039 import java.util.Map;
040 import java.util.concurrent.ConcurrentHashMap;
041
042 public class CRaSH {
043
044 /** . */
045 private static final HashMap<String, Class<? extends BaseCommand>> systemCommands = new HashMap<String, Class<? extends BaseCommand>>();
046
047 static {
048 systemCommands.put("help", help.class);
049 systemCommands.put("repl", repl.class);
050 }
051
052 /** . */
053 final PluginContext context;
054
055 /** . */
056 final HashMap<String, CommandManager> activeManagers;
057
058 /** . */
059 private final Map<String, TimestampedObject<CommandResolution>> commandCache = new ConcurrentHashMap<String, TimestampedObject<CommandResolution>>();
060
061 /**
062 * Create a new CRaSH.
063 *
064 * @param context the plugin context
065 * @throws NullPointerException if the context argument is null
066 */
067 public CRaSH(PluginContext context) throws NullPointerException {
068
069 //
070 HashMap<String, CommandManager> activeManagers = new HashMap<String, CommandManager>();
071 for (CommandManager manager : context.getPlugins(CommandManager.class)) {
072 if (manager.isActive()) {
073 for (String ext : manager.getExtensions()) {
074 activeManagers.put(ext, manager);
075 }
076 }
077 }
078
079
080 this.context = context;
081 this.activeManagers = activeManagers;
082 }
083
084 public CRaSHSession createSession(Principal user) {
085 return new CRaSHSession(this, user);
086 }
087
088 /**
089 * Returns the plugin context.
090 *
091 * @return the plugin context
092 */
093 public PluginContext getContext() {
094 return context;
095 }
096
097 /**
098 * Attempt to obtain a command description. Null is returned when such command does not exist.
099 *
100 * @param name the command name
101 * @return a command description
102 * @throws org.crsh.shell.impl.command.spi.CommandCreationException if an error occured preventing the command creation
103 * @throws NullPointerException if the name argument is null
104 */
105 public String getCommandDescription(String name) throws CommandCreationException, NullPointerException {
106 CommandResolution resolution = resolveCommand(name);
107 return resolution != null ? resolution.getDescription() : null;
108 }
109
110 /**
111 * Attempt to obtain a command instance. Null is returned when such command does not exist.
112 *
113 * @param name the command name
114 * @return a command instance
115 * @throws org.crsh.shell.impl.command.spi.CommandCreationException if an error occured preventing the command creation
116 * @throws NullPointerException if the name argument is null
117 */
118 public ShellCommand<?> getCommand(String name) throws CommandCreationException, NullPointerException {
119 CommandResolution resolution = resolveCommand(name);
120 return resolution != null ? resolution.getCommand() : null;
121 }
122
123 /**
124 * Attempt to obtain a command instance. Null is returned when such command does not exist.
125 *
126 * @param name the command name
127 * @return a command instance
128 * @throws org.crsh.shell.impl.command.spi.CommandCreationException if an error occured preventing the command creation
129 * @throws NullPointerException if the name argument is null
130 */
131 public CommandResolution resolveCommand(final String name) throws CommandCreationException, NullPointerException {
132 if (name == null) {
133 throw new NullPointerException("No null name accepted");
134 }
135 final Class<? extends BaseCommand> systemCommand = systemCommands.get(name);
136 if (systemCommand != null) {
137 return createCommand(systemCommand);
138 } else {
139 for (CommandManager manager : activeManagers.values()) {
140 if (manager.isActive()) {
141 for (String ext : manager.getExtensions()) {
142 Iterable<Resource> resources = context.loadResources(name + "." + ext, ResourceKind.COMMAND);
143 for (Resource resource : resources) {
144 CommandResolution resolution = resolveCommand(manager, name, resource);
145 if (resolution != null) {
146 return resolution;
147 }
148 }
149 }
150 }
151 }
152 return null;
153 }
154 }
155
156 public Iterable<String> getCommandNames() {
157 LinkedHashSet<String> names = new LinkedHashSet<String>(systemCommands.keySet());
158 for (String resourceName : context.listResources(ResourceKind.COMMAND)) {
159 int index = resourceName.indexOf('.');
160 String name = resourceName.substring(0, index);
161 String ext = resourceName.substring(index + 1);
162 if (activeManagers.containsKey(ext)) {
163 names.add(name);
164 }
165 }
166 return names;
167 }
168
169 private CommandResolution resolveCommand(CommandManager manager, String name, Resource script) throws CommandCreationException {
170 TimestampedObject<CommandResolution> ref = commandCache.get(name);
171 if (ref != null) {
172 if (script.getTimestamp() != ref.getTimestamp()) {
173 ref = null;
174 }
175 }
176 CommandResolution command;
177 if (ref == null) {
178 command = manager.resolveCommand(name, script.getContent());
179 if (command != null) {
180 commandCache.put(name, new TimestampedObject<CommandResolution>(script.getTimestamp(), command));
181 }
182 } else {
183 command = ref.getObject();
184 }
185 return command;
186 }
187
188 private <C extends BaseCommand> CommandResolution createCommand(final Class<C> commandClass) {
189 return new CommandResolution() {
190 final ShellCommandImpl<C> shellCommand = new ShellCommandImpl<C>(commandClass);
191 @Override
192 public String getDescription() {
193 return shellCommand.describe(commandClass.getSimpleName(), Format.DESCRIBE);
194 }
195 @Override
196 public ShellCommand<?> getCommand() throws CommandCreationException {
197 return shellCommand;
198 }
199 };
200 }
201 }