1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.frontend.cli;
20
21 import java.lang.reflect.Modifier;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Optional;
29 import java.util.TreeMap;
30 import java.util.stream.Collectors;
31 import java.util.stream.Stream;
32
33 import org.apache.logging.log4j.LogManager;
34 import org.apache.logging.log4j.Logger;
35 import org.mycore.common.MCRClassTools;
36 import org.mycore.common.config.MCRConfiguration2;
37 import org.mycore.common.config.MCRConfigurationException;
38 import org.mycore.frontend.cli.annotation.MCRCommandGroup;
39
40
41
42
43
44
45
46 public class MCRCommandManager {
47 private static final Logger LOGGER = LogManager.getLogger(MCRCommandManager.class);
48
49 protected static TreeMap<String, List<MCRCommand>> knownCommands = new TreeMap<>();
50
51 public MCRCommandManager() {
52 try {
53 initBuiltInCommands();
54 initCommands();
55 } catch (Exception ex) {
56 handleInitException(ex);
57 }
58 }
59
60 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings("DM_EXIT")
61 protected void handleInitException(Exception ex) {
62 MCRCLIExceptionHandler.handleException(ex);
63 System.exit(1);
64 }
65
66 public static TreeMap<String, List<MCRCommand>> getKnownCommands() {
67 return knownCommands;
68 }
69
70 protected void initBuiltInCommands() {
71 addAnnotatedCLIClass(MCRBasicCommands.class);
72 }
73
74 protected void initCommands() {
75
76 initConfiguredCommands("Internal");
77 initConfiguredCommands("External");
78 }
79
80
81 protected void initConfiguredCommands(String type) {
82 String prefix = "MCR.CLI.Classes." + type;
83 Stream<Map.Entry<String, String>> propsWithPrefix = MCRConfiguration2.getPropertiesMap()
84 .entrySet()
85 .stream()
86 .filter(e -> e.getKey().startsWith(prefix));
87 Stream<String> classNames = propsWithPrefix
88 .map(Map.Entry::getValue)
89 .flatMap(MCRConfiguration2::splitValue)
90 .filter(s -> !s.isEmpty());
91 classNames.forEach(this::loadCommandClass);
92 }
93
94 private void loadCommandClass(String commandClassName) {
95 LOGGER.debug("Will load commands from the {} class {}", commandClassName, commandClassName);
96 try {
97 Class<?> cliClass = MCRClassTools.forName(commandClassName);
98 if (cliClass.isAnnotationPresent(MCRCommandGroup.class)) {
99 addAnnotatedCLIClass(cliClass);
100 } else {
101 addDefaultCLIClass(commandClassName);
102 }
103
104 } catch (ClassNotFoundException cnfe) {
105 LOGGER.error("MyCoRe Command Class {} not found.", commandClassName);
106 }
107 }
108
109 protected void addAnnotatedCLIClass(Class<?> cliClass) {
110 String groupName = Optional.ofNullable(cliClass.getAnnotation(MCRCommandGroup.class))
111 .map(MCRCommandGroup::name)
112 .orElse(cliClass.getSimpleName());
113 final Class<org.mycore.frontend.cli.annotation.MCRCommand> mcrCommandAnnotation;
114 mcrCommandAnnotation = org.mycore.frontend.cli.annotation.MCRCommand.class;
115 ArrayList<MCRCommand> commands = Arrays.stream(cliClass.getMethods())
116 .filter(method -> method.getDeclaringClass().equals(cliClass))
117 .filter(method -> Modifier.isStatic(method.getModifiers()) && Modifier.isPublic(method.getModifiers()))
118 .filter(method -> method.isAnnotationPresent(mcrCommandAnnotation))
119 .sorted(Comparator.comparingInt(m -> m.getAnnotation(mcrCommandAnnotation).order()))
120 .map(MCRCommand::new)
121 .collect(Collectors.toCollection(ArrayList::new));
122 addCommandGroup(groupName, commands);
123 }
124
125
126 private List<MCRCommand> addCommandGroup(String groupName, ArrayList<MCRCommand> commands) {
127 return knownCommands.put(groupName, Collections.unmodifiableList(commands));
128 }
129
130 protected void addDefaultCLIClass(String className) {
131 Object obj = buildInstanceOfClass(className);
132 MCRExternalCommandInterface commandInterface = (MCRExternalCommandInterface) obj;
133 ArrayList<MCRCommand> commandsToAdd = commandInterface.getPossibleCommands();
134 addCommandGroup(commandInterface.getDisplayName(), commandsToAdd);
135 }
136
137 private Object buildInstanceOfClass(String className) {
138 try {
139 return MCRClassTools.forName(className).getDeclaredConstructor().newInstance();
140 } catch (Exception ex) {
141 String msg = "Could not instantiate class " + className;
142 throw new MCRConfigurationException(msg, ex);
143 }
144 }
145
146 public List<String> invokeCommand(String command) throws Exception {
147 if (command.trim().startsWith("#")) {
148
149 return new ArrayList<String>();
150 }
151 for (List<MCRCommand> commands : knownCommands.values()) {
152 for (MCRCommand currentCommand : commands) {
153 long start = System.currentTimeMillis();
154 List<String> commandsReturned = currentCommand.invoke(command);
155 long end = System.currentTimeMillis();
156
157 if (commandsReturned != null) {
158 long timeNeeded = end - start;
159 MCRCommandLineInterface.output("Command processed (" + timeNeeded + " ms)");
160 MCRCommandStatistics.commandInvoked(currentCommand, timeNeeded);
161
162 return commandsReturned;
163 }
164 }
165 }
166
167 MCRCommandLineInterface.output("Command not understood:" + command);
168 MCRCommandLineInterface.output("Enter 'help' to get a list of commands.");
169 return new ArrayList<>();
170 }
171 }