| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sling.cli.impl; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import org.apache.sling.cli.impl.release.ReleaseCLIGroup; |
| import org.jetbrains.annotations.NotNull; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.launch.Framework; |
| import org.osgi.service.component.annotations.Activate; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Reference; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import picocli.CommandLine; |
| |
| import static org.osgi.service.component.annotations.ReferenceCardinality.MULTIPLE; |
| import static org.osgi.service.component.annotations.ReferencePolicy.DYNAMIC; |
| |
| @CommandLine.Command( |
| name = "docker run -it --env-file=./docker-env apache/sling-cli", |
| description = "Apache Sling Committers CLI" |
| ) |
| @Component(service = CommandProcessor.class) |
| public class CommandProcessor { |
| |
| private final Logger logger = LoggerFactory.getLogger(getClass()); |
| private static final String EXEC_ARGS = "exec.args"; |
| private BundleContext ctx; |
| |
| private static final Map<String, Class> CLI_GROUPS; |
| |
| static { |
| CLI_GROUPS = new HashMap<>(); |
| CLI_GROUPS.put("release", ReleaseCLIGroup.class); |
| } |
| |
| private Map<String, TreeSet<CommandWithProps>> commands = new ConcurrentHashMap<>(); |
| |
| @Activate |
| private void activate(BundleContext ctx) { |
| this.ctx = ctx; |
| } |
| |
| @Reference(service = Command.class, cardinality = MULTIPLE, policy = DYNAMIC) |
| protected void bindCommand(Command cmd, Map<String, ?> props) { |
| CommandWithProps commandWithProps = CommandWithProps.of(cmd, props); |
| Set<CommandWithProps> bucket = commands.computeIfAbsent(commandWithProps.group, key -> new TreeSet<>()); |
| bucket.add(commandWithProps); |
| } |
| |
| protected void unbindCommand(Command cmd, Map<String, ?> props) { |
| CommandWithProps commandWithProps = CommandWithProps.of(cmd, props); |
| Set<CommandWithProps> bucket = commands.get(commandWithProps.group); |
| if (bucket != null) { |
| bucket.remove(commandWithProps); |
| if (bucket.isEmpty()) { |
| commands.remove(commandWithProps.group); |
| } |
| } |
| |
| } |
| |
| void runCommand() { |
| System.setProperty("picocli.usage.width", "140"); |
| CommandLine commandLine = new CommandLine(this); |
| commandLine.addSubcommand(CommandLine.HelpCommand.class); |
| for (Map.Entry<String, TreeSet<CommandWithProps>> entry : commands.entrySet()) { |
| String group = entry.getKey(); |
| Class<?> groupClass = CLI_GROUPS.get(group); |
| if (groupClass != null) { |
| CommandLine secondary = new CommandLine(groupClass); |
| for (CommandWithProps command : entry.getValue()) { |
| secondary.addSubcommand(command.name, command.cmd); |
| } |
| secondary.addSubcommand(CommandLine.HelpCommand.class); |
| commandLine.addSubcommand(group, secondary); |
| } else { |
| for (CommandWithProps command : entry.getValue()) { |
| commandLine.addSubcommand(command.group, command.cmd); |
| } |
| } |
| } |
| int commandExitCode; |
| try { |
| String[] arguments = arguments(ctx.getProperty(EXEC_ARGS)); |
| commandExitCode = commandLine.execute(arguments); |
| } catch (CommandLine.ParameterException e) { |
| commandLine.getErr().println(e.getMessage()); |
| if (!CommandLine.UnmatchedArgumentException.printSuggestions(e, commandLine.getErr())) { |
| e.getCommandLine().usage(commandLine.getErr()); |
| } |
| commandExitCode = commandLine.getCommandSpec().exitCodeOnInvalidInput(); |
| } catch (Exception e) { |
| logger.warn("Failed running command.", e); |
| commandExitCode = 1; |
| } finally { |
| try { |
| ctx.getBundle(Constants.SYSTEM_BUNDLE_LOCATION).adapt(Framework.class).stop(); |
| } catch (BundleException e) { |
| logger.error("Failed shutting down framework, forcing exit", e); |
| System.exit(1); |
| } |
| } |
| System.exit(commandExitCode); |
| } |
| |
| private String[] arguments(String cliSpec) { |
| if (cliSpec == null) { |
| return new String[0]; |
| } |
| return cliSpec.split(" "); |
| } |
| |
| static class CommandWithProps implements Comparable<CommandWithProps> { |
| private final String group; |
| private final String name; |
| private final Command cmd; |
| |
| static CommandWithProps of(Command cmd, Map<String, ?> props) { |
| return new CommandWithProps( |
| cmd, |
| (String) props.get(Command.PROPERTY_NAME_COMMAND_GROUP), |
| (String) props.get(Command.PROPERTY_NAME_COMMAND_NAME) |
| ); |
| } |
| |
| CommandWithProps(Command cmd, String group, String name) { |
| this.cmd = cmd; |
| this.group = group; |
| this.name = name; |
| } |
| |
| @Override |
| public int compareTo(@NotNull CommandProcessor.CommandWithProps o) { |
| if (!group.equals(o.group)) { |
| return group.compareTo(o.group); |
| } else { |
| return name.compareTo(o.name); |
| } |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(group, name); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null) { |
| return false; |
| } |
| if (obj instanceof CommandWithProps) { |
| CommandWithProps other = (CommandWithProps) obj; |
| return Objects.equals(group, other.group) && Objects.equals(name, other.name); |
| } |
| return false; |
| } |
| } |
| } |