001/*
002 * (C) Copyright 2006-2015 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 */
020
021package org.nuxeo.ecm.platform.commandline.executor.service;
022
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027
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.model.ComponentContext;
042import org.nuxeo.runtime.model.ComponentInstance;
043import org.nuxeo.runtime.model.DefaultComponent;
044
045/**
046 * POJO implementation of the {@link CommandLineExecutorService} interface. Also handles the Extension Point logic.
047 *
048 * @author tiry
049 */
050public class CommandLineExecutorComponent extends DefaultComponent implements CommandLineExecutorService {
051
052    public static final String EP_ENV = "environment";
053
054    public static final String EP_CMD = "command";
055
056    public static final String EP_CMDTESTER = "commandTester";
057
058    public static final String DEFAULT_TESTER = "DefaultCommandTester";
059
060    public static final String DEFAULT_EXECUTOR = "ShellExecutor";
061
062    protected static Map<String, CommandLineDescriptor> commandDescriptors = new HashMap<>();
063
064    protected static EnvironmentDescriptor env = new EnvironmentDescriptor();
065
066    protected static Map<String, EnvironmentDescriptor> envDescriptors = new HashMap<>();
067
068    protected static Map<String, CommandTester> testers = new HashMap<>();
069
070    protected static Map<String, Executor> executors = new HashMap<>();
071
072    private static final Log log = LogFactory.getLog(CommandLineExecutorComponent.class);
073
074    @Override
075    public void activate(ComponentContext context) {
076        commandDescriptors = new HashMap<>();
077        env = new EnvironmentDescriptor();
078        testers = new HashMap<>();
079        executors = new HashMap<>();
080        executors.put(DEFAULT_EXECUTOR, new ShellExecutor());
081    }
082
083    @Override
084    public void deactivate(ComponentContext context) {
085        commandDescriptors = null;
086        env = null;
087        testers = null;
088        executors = null;
089    }
090
091    @Override
092    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
093        if (EP_ENV.equals(extensionPoint)) {
094            EnvironmentDescriptor desc = (EnvironmentDescriptor) contribution;
095            String name = desc.getName();
096            if (name == null) {
097                env.merge(desc);
098            } else {
099                for (String envName : name.split(",")) {
100                    if (envDescriptors.containsKey(envName)) {
101                        envDescriptors.get(envName).merge(desc);
102                    } else {
103                        envDescriptors.put(envName, desc);
104                    }
105                }
106            }
107        } else if (EP_CMD.equals(extensionPoint)) {
108            CommandLineDescriptor desc = (CommandLineDescriptor) contribution;
109            String name = desc.getName();
110
111            log.debug("Registering command: " + name);
112
113            if (!desc.isEnabled()) {
114                commandDescriptors.remove(name);
115                log.info("Command configured to not be enabled: " + name);
116                return;
117            }
118
119            String testerName = desc.getTester();
120            if (testerName == null) {
121                testerName = DEFAULT_TESTER;
122                log.debug("Using default tester for command: " + name);
123            }
124
125            CommandTester tester = testers.get(testerName);
126            boolean cmdAvailable = false;
127            if (tester == null) {
128                log.error("Unable to find tester '" + testerName + "', command will not be available: " + name);
129            } else {
130                log.debug("Using tester '" + testerName + "' for command: " + name);
131                CommandTestResult testResult = tester.test(desc);
132                cmdAvailable = testResult.succeed();
133                if (cmdAvailable) {
134                    log.info("Registered command: " + name);
135                } else {
136                    desc.setInstallErrorMessage(testResult.getErrorMessage());
137                    log.warn("Command not available: " + name + " (" + desc.getInstallErrorMessage() + ". "
138                            + desc.getInstallationDirective() + ')');
139                }
140            }
141            desc.setAvailable(cmdAvailable);
142            commandDescriptors.put(name, desc);
143        } else if (EP_CMDTESTER.equals(extensionPoint)) {
144            CommandTesterDescriptor desc = (CommandTesterDescriptor) contribution;
145            CommandTester tester;
146            try {
147                tester = (CommandTester) desc.getTesterClass().newInstance();
148            } catch (ReflectiveOperationException e) {
149                throw new RuntimeException(e);
150            }
151            testers.put(desc.getName(), tester);
152        }
153    }
154
155    @Override
156    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
157    }
158
159    /*
160     * Service interface
161     */
162    @Override
163    public ExecResult execCommand(String commandName, CmdParameters params) throws CommandNotAvailable {
164        CommandAvailability availability = getCommandAvailability(commandName);
165        if (!availability.isAvailable()) {
166            throw new CommandNotAvailable(availability);
167        }
168
169        CommandLineDescriptor cmdDesc = commandDescriptors.get(commandName);
170        Executor executor = executors.get(cmdDesc.getExecutor());
171        EnvironmentDescriptor environment = new EnvironmentDescriptor().merge(env).merge(
172                envDescriptors.getOrDefault(commandName, envDescriptors.get(cmdDesc.getCommand())));
173        return executor.exec(cmdDesc, params, environment);
174    }
175
176    @Override
177    public CommandAvailability getCommandAvailability(String commandName) {
178        if (!commandDescriptors.containsKey(commandName)) {
179            return new CommandAvailability(commandName + " is not a registered command");
180        }
181
182        CommandLineDescriptor desc = commandDescriptors.get(commandName);
183        if (desc.isAvailable()) {
184            return new CommandAvailability();
185        } else {
186            return new CommandAvailability(desc.getInstallationDirective(), desc.getInstallErrorMessage());
187        }
188    }
189
190    @Override
191    public List<String> getRegistredCommands() {
192        List<String> cmds = new ArrayList<>();
193        cmds.addAll(commandDescriptors.keySet());
194        return cmds;
195    }
196
197    @Override
198    public List<String> getAvailableCommands() {
199        List<String> cmds = new ArrayList<>();
200
201        for (String cmdName : commandDescriptors.keySet()) {
202            CommandLineDescriptor cmd = commandDescriptors.get(cmdName);
203            if (cmd.isAvailable()) {
204                cmds.add(cmdName);
205            }
206        }
207        return cmds;
208    }
209
210    // ******************************************
211    // for testing
212
213    public static CommandLineDescriptor getCommandDescriptor(String commandName) {
214        return commandDescriptors.get(commandName);
215    }
216
217    @Override
218    public CmdParameters getDefaultCmdParameters() {
219        CmdParameters params = new CmdParameters();
220        params.addNamedParameter("java.io.tmpdir", System.getProperty("java.io.tmpdir"));
221        params.addNamedParameter(Environment.NUXEO_TMP_DIR, Environment.getDefault().getTemp().getPath());
222        return params;
223    }
224
225}