001/* 002 * (C) Copyright 2006-2015 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library 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 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 */ 018 019package org.nuxeo.ecm.platform.commandline.executor.service; 020 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.regex.Pattern; 026 027import org.apache.commons.lang3.SystemUtils; 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030 031import org.nuxeo.common.Environment; 032import org.nuxeo.ecm.platform.commandline.executor.api.CmdParameters; 033import org.nuxeo.ecm.platform.commandline.executor.api.CommandAvailability; 034import org.nuxeo.ecm.platform.commandline.executor.api.CommandLineExecutorService; 035import org.nuxeo.ecm.platform.commandline.executor.api.CommandNotAvailable; 036import org.nuxeo.ecm.platform.commandline.executor.api.ExecResult; 037import org.nuxeo.ecm.platform.commandline.executor.service.cmdtesters.CommandTestResult; 038import org.nuxeo.ecm.platform.commandline.executor.service.cmdtesters.CommandTester; 039import org.nuxeo.ecm.platform.commandline.executor.service.executors.Executor; 040import org.nuxeo.ecm.platform.commandline.executor.service.executors.ShellExecutor; 041import org.nuxeo.runtime.api.Framework; 042import org.nuxeo.runtime.model.ComponentContext; 043import org.nuxeo.runtime.model.ComponentInstance; 044import org.nuxeo.runtime.model.DefaultComponent; 045 046/** 047 * POJO implementation of the {@link CommandLineExecutorService} interface. Also handles the Extension Point logic. 048 * 049 * @author tiry 050 */ 051public class CommandLineExecutorComponent extends DefaultComponent implements CommandLineExecutorService { 052 053 public static final String EP_ENV = "environment"; 054 055 public static final String EP_CMD = "command"; 056 057 public static final String EP_CMDTESTER = "commandTester"; 058 059 public static final String DEFAULT_TESTER = "SystemPathTester"; 060 061 public static final String DEFAULT_EXECUTOR = "ShellExecutor"; 062 063 protected static Map<String, CommandLineDescriptor> commandDescriptors = new HashMap<>(); 064 065 protected static EnvironmentDescriptor env = new EnvironmentDescriptor(); 066 067 protected static Map<String, EnvironmentDescriptor> envDescriptors = new HashMap<>(); 068 069 protected static Map<String, CommandTester> testers = new HashMap<>(); 070 071 protected static Map<String, Executor> executors = new HashMap<>(); 072 073 private static final Log log = LogFactory.getLog(CommandLineExecutorComponent.class); 074 075 @Override 076 public void activate(ComponentContext context) { 077 commandDescriptors = new HashMap<>(); 078 env = new EnvironmentDescriptor(); 079 testers = new HashMap<>(); 080 executors = new HashMap<>(); 081 executors.put(DEFAULT_EXECUTOR, new ShellExecutor()); 082 } 083 084 @Override 085 public void deactivate(ComponentContext context) { 086 commandDescriptors = null; 087 env = null; 088 testers = null; 089 executors = null; 090 } 091 092 @Override 093 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 094 if (EP_ENV.equals(extensionPoint)) { 095 EnvironmentDescriptor desc = (EnvironmentDescriptor) contribution; 096 String name = desc.getName(); 097 if (name == null) { 098 env.merge(desc); 099 } else { 100 for (String envName : name.split(",")) { 101 if (envDescriptors.containsKey(envName)) { 102 envDescriptors.get(envName).merge(desc); 103 } else { 104 envDescriptors.put(envName, desc); 105 } 106 } 107 } 108 } else if (EP_CMD.equals(extensionPoint)) { 109 CommandLineDescriptor desc = (CommandLineDescriptor) contribution; 110 String name = desc.getName(); 111 112 log.debug("Registering command: " + name); 113 114 if (!desc.isEnabled()) { 115 commandDescriptors.remove(name); 116 log.info("Command configured to not be enabled: " + name); 117 return; 118 } 119 120 String testerName = desc.getTester(); 121 if (testerName == null) { 122 testerName = DEFAULT_TESTER; 123 log.debug("Using default tester for command: " + name); 124 } 125 126 CommandTester tester = testers.get(testerName); 127 boolean cmdAvailable = false; 128 if (tester == null) { 129 log.error("Unable to find tester '" + testerName + "', command will not be available: " + name); 130 } else { 131 log.debug("Using tester '" + testerName + "' for command: " + name); 132 CommandTestResult testResult = tester.test(desc); 133 cmdAvailable = testResult.succeed(); 134 if (cmdAvailable) { 135 log.info("Registered command: " + name); 136 } else { 137 desc.setInstallErrorMessage(testResult.getErrorMessage()); 138 log.warn("Command not available: " + name + " (" + desc.getInstallErrorMessage() + ". " 139 + desc.getInstallationDirective() + ')'); 140 } 141 } 142 desc.setAvailable(cmdAvailable); 143 commandDescriptors.put(name, desc); 144 } else if (EP_CMDTESTER.equals(extensionPoint)) { 145 CommandTesterDescriptor desc = (CommandTesterDescriptor) contribution; 146 CommandTester tester; 147 try { 148 tester = (CommandTester) desc.getTesterClass().newInstance(); 149 } catch (ReflectiveOperationException e) { 150 throw new RuntimeException(e); 151 } 152 testers.put(desc.getName(), tester); 153 } 154 } 155 156 @Override 157 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 158 } 159 160 /* 161 * Service interface 162 */ 163 @Override 164 public ExecResult execCommand(String commandName, CmdParameters params) throws CommandNotAvailable { 165 CommandAvailability availability = getCommandAvailability(commandName); 166 if (!availability.isAvailable()) { 167 throw new CommandNotAvailable(availability); 168 } 169 170 CommandLineDescriptor cmdDesc = commandDescriptors.get(commandName); 171 Executor executor = executors.get(cmdDesc.getExecutor()); 172 EnvironmentDescriptor environment = new EnvironmentDescriptor().merge(env).merge( 173 envDescriptors.getOrDefault(commandName, envDescriptors.get(cmdDesc.getCommand()))); 174 return executor.exec(cmdDesc, params, environment); 175 } 176 177 @Override 178 public CommandAvailability getCommandAvailability(String commandName) { 179 if (!commandDescriptors.containsKey(commandName)) { 180 return new CommandAvailability(commandName + " is not a registered command"); 181 } 182 183 CommandLineDescriptor desc = commandDescriptors.get(commandName); 184 if (desc.isAvailable()) { 185 return new CommandAvailability(); 186 } else { 187 return new CommandAvailability(desc.getInstallationDirective(), desc.getInstallErrorMessage()); 188 } 189 } 190 191 @Override 192 public List<String> getRegistredCommands() { 193 List<String> cmds = new ArrayList<>(); 194 cmds.addAll(commandDescriptors.keySet()); 195 return cmds; 196 } 197 198 @Override 199 public List<String> getAvailableCommands() { 200 List<String> cmds = new ArrayList<>(); 201 202 for (String cmdName : commandDescriptors.keySet()) { 203 CommandLineDescriptor cmd = commandDescriptors.get(cmdName); 204 if (cmd.isAvailable()) { 205 cmds.add(cmdName); 206 } 207 } 208 return cmds; 209 } 210 211 @Override 212 public boolean isValidParameter(String parameter) { 213 Pattern VALID_PATTERN; 214 if (SystemUtils.IS_OS_WINDOWS) { 215 VALID_PATTERN = VALID_PARAMETER_PATTERN_WIN; 216 } else { 217 VALID_PATTERN = VALID_PARAMETER_PATTERN; 218 } 219 return VALID_PATTERN.matcher(parameter).matches(); 220 } 221 222 @Override 223 public void checkParameter(String parameter) { 224 if (!isValidParameter(parameter)) { 225 Pattern VALID_PATTERN; 226 if (SystemUtils.IS_OS_WINDOWS) { 227 VALID_PATTERN = VALID_PARAMETER_PATTERN_WIN; 228 } else { 229 VALID_PATTERN = VALID_PARAMETER_PATTERN; 230 } 231 throw new IllegalArgumentException(String.format("'%s' contains illegal characters. It should match: %s", 232 parameter, VALID_PATTERN)); 233 } 234 } 235 236 // ****************************************** 237 // for testing 238 239 public static CommandLineDescriptor getCommandDescriptor(String commandName) { 240 return commandDescriptors.get(commandName); 241 } 242 243 @Override 244 public CmdParameters getDefaultCmdParameters() { 245 CmdParameters params = new CmdParameters(); 246 params.addNamedParameter("java.io.tmpdir", System.getProperty("java.io.tmpdir")); 247 params.addNamedParameter(Environment.NUXEO_TMP_DIR, Framework.getProperty(Environment.NUXEO_TMP_DIR)); 248 return params; 249 } 250 251}