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 020package org.crsh.shell.impl.command; 021 022import org.crsh.cli.descriptor.Format; 023import org.crsh.command.BaseCommand; 024import org.crsh.lang.java.ShellCommandImpl; 025import org.crsh.shell.impl.command.spi.CommandCreationException; 026import org.crsh.shell.impl.command.spi.ShellCommand; 027import org.crsh.plugin.PluginContext; 028import org.crsh.plugin.ResourceKind; 029import org.crsh.shell.impl.command.spi.CommandManager; 030import org.crsh.shell.impl.command.spi.CommandResolution; 031import org.crsh.shell.impl.command.system.help; 032import org.crsh.shell.impl.command.system.repl; 033import org.crsh.util.TimestampedObject; 034import org.crsh.vfs.Resource; 035 036import java.security.Principal; 037import java.util.HashMap; 038import java.util.LinkedHashSet; 039import java.util.Map; 040import java.util.concurrent.ConcurrentHashMap; 041 042public 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}