Impl Verify feature for CLI
[cli.git] / framework / src / main / java / org / onap / cli / fw / registrar / OnapCommandRegistrar.java
1 /*
2  * Copyright 2017 Huawei Technologies Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onap.cli.fw.registrar;
18
19 import org.apache.commons.io.IOUtils;
20 import org.onap.cli.fw.cmd.OnapCommand;
21 import org.onap.cli.fw.conf.OnapCommandConfig;
22 import org.onap.cli.fw.conf.OnapCommandConstants;
23 import org.onap.cli.fw.error.OnapCommandException;
24 import org.onap.cli.fw.error.OnapCommandHelpFailed;
25 import org.onap.cli.fw.error.OnapCommandInvalidRegistration;
26 import org.onap.cli.fw.error.OnapCommandNotFound;
27 import org.onap.cli.fw.error.OnapCommandProductVersionInvalid;
28 import org.onap.cli.fw.error.OnapCommandRegistrationProductInfoMissing;
29 import org.onap.cli.fw.error.OnapUnsupportedSchemaProfile;
30 import org.onap.cli.fw.input.cache.OnapCommandParameterCache;
31 import org.onap.cli.fw.output.OnapCommandPrintDirection;
32 import org.onap.cli.fw.output.OnapCommandResult;
33 import org.onap.cli.fw.output.OnapCommandResultAttribute;
34 import org.onap.cli.fw.output.OnapCommandResultAttributeScope;
35 import org.onap.cli.fw.output.OnapCommandResultType;
36 import org.onap.cli.fw.schema.OnapCommandSchema;
37 import org.onap.cli.fw.schema.OnapCommandSchemaInfo;
38 import org.onap.cli.fw.utils.OnapCommandDiscoveryUtils;
39 import org.onap.cli.fw.utils.OnapCommandHelperUtils;
40 import org.onap.cli.fw.utils.OnapCommandUtils;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import java.io.IOException;
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Set;
50
51
52 /**
53  * Oclip Command registrar provides a common place, where every command would get registered automatically when its
54  * loaded into JVM.
55  *
56  */
57 public class OnapCommandRegistrar {
58
59     private static Logger LOG = LoggerFactory.getLogger(OnapCommandRegistrar.class);
60
61     private Map<String, Class<? extends OnapCommand>> registry = new HashMap<>();
62
63     private Map<String, Class<? extends OnapCommand>> registryProfilePlugins = new HashMap<>();
64
65     private Set<String> availableProductVersions = new HashSet<>();
66
67     private String enabledProductVersion = null;
68
69     private boolean isInteractiveMode = false;
70
71     private OnapCommandParameterCache paramCache = OnapCommandParameterCache.getInstance();
72
73     public boolean isInteractiveMode() {
74         return isInteractiveMode;
75     }
76
77     public void setInteractiveMode(boolean isInteractiveMode) {
78         this.isInteractiveMode = isInteractiveMode;
79     }
80
81     public Map<String, String> getParamCache() {
82         return paramCache.getParams(this.getEnabledProductVersion());
83     }
84
85     public void addParamCache(String paramName, String paramValue) {
86         paramCache.add(this.getEnabledProductVersion(), paramName, paramValue);
87     }
88
89     public void removeParamCache(String paramName) {
90         paramCache.remove(this.getEnabledProductVersion(), paramName);
91     }
92
93     public void setProfile(String profileName, List<String> includes, List<String> excludes) {
94         this.paramCache.setProfile(profileName);
95
96         for (String profile : includes) {
97             this.paramCache.includeProfile(profile);
98         }
99
100         for (String profile : excludes) {
101             this.paramCache.excludeProfile(profile);
102         }
103     }
104
105     public List<String> getUserProfiles() {
106         return paramCache.getProfiles();
107     }
108
109     private static OnapCommandRegistrar registrar = null;
110
111     /**
112      * Register the command into registrar and throws OnapInvalidCommandRegistration for invalid command.
113      *
114      * @param name Command Name
115      * @param cmd  Command Class
116      * @throws OnapCommandInvalidRegistration            Invalid registration exception
117      * @throws OnapCommandRegistrationProductInfoMissing
118      */
119     private void register(String name, String version, Class<? extends OnapCommand> cmd) throws OnapCommandInvalidRegistration, OnapCommandRegistrationProductInfoMissing {
120         if (version == null || version.isEmpty()) {
121             throw new OnapCommandRegistrationProductInfoMissing(name);
122         }
123
124         this.registry.put(name + ":" + version, cmd);
125         this.availableProductVersions.add(version);
126
127     }
128
129     private void registerProfilePlugin(String profile, Class<? extends OnapCommand> cmd) {
130         this.registryProfilePlugins.put(profile, cmd);
131     }
132
133     private OnapCommandRegistrar() {
134         this.enabledProductVersion = System.getenv(OnapCommandConstants.OPEN_CLI_PRODUCT_IN_USE_ENV_NAME);
135         if (this.enabledProductVersion == null) {
136             this.enabledProductVersion = OnapCommandConfig.getPropertyValue(OnapCommandConstants.OPEN_CLI_PRODUCT_NAME);
137         }
138     }
139
140     /**
141      * Get global registrar.
142      *
143      * @throws OnapCommandException exception
144      */
145     public static OnapCommandRegistrar getRegistrar() throws OnapCommandException {
146         if (registrar == null) {
147             registrar = new OnapCommandRegistrar();
148             registrar.autoDiscoverSchemas();
149         }
150
151         return registrar;
152     }
153
154     /**
155      * Get the list of discovered commands by registrar.
156      *
157      * @return set
158      */
159     public Set<String> listCommands() {
160         return this.registry.keySet();
161     }
162
163     /**
164      * Get the list of discovered commands for a given product version in registrar.
165      *
166      * @return set
167      */
168     public Set<String> listCommandsForEnabledProductVersion() {
169         String version = this.getEnabledProductVersion();
170
171         Set<String> cmds = new HashSet<>();
172         if (!this.availableProductVersions.contains(version)) {
173             return cmds;
174         }
175
176         for (String cmd : this.registry.keySet()) {
177             if (cmd.split(":")[1].equalsIgnoreCase(version)) {
178                 cmds.add(cmd.split(":")[0]);
179             }
180         }
181         return cmds;
182     }
183
184     public Class<? extends OnapCommand> getProfilePlugin(String profile) throws OnapUnsupportedSchemaProfile {
185         if (!this.registryProfilePlugins.containsKey(profile)) {
186             throw new OnapUnsupportedSchemaProfile(profile);
187         }
188
189         return this.registryProfilePlugins.get(profile);
190     }
191
192     public Set<String> getAvailableProductVersions() {
193         return this.availableProductVersions;
194     }
195
196     public void setEnabledProductVersion(String version) throws OnapCommandProductVersionInvalid {
197         if (!this.availableProductVersions.contains(version)) {
198             throw new OnapCommandProductVersionInvalid(version, availableProductVersions);
199         }
200
201         this.enabledProductVersion = version;
202     }
203
204     public String getEnabledProductVersion() {
205         return this.enabledProductVersion;
206     }
207
208     /**
209      * Returns command details.
210      *
211      * @return map
212      * @throws OnapCommandException exception
213      */
214     public List<OnapCommandSchemaInfo> listCommandInfo() throws OnapCommandException {
215         return OnapCommandDiscoveryUtils.discoverSchemas();
216     }
217
218     /**
219      * Get the OnapCommand, which CLI main would use to find the command based on the command name.
220      *
221      * @param cmdName Name of command
222      * @return OnapCommand
223      * @throws OnapCommandException Exception
224      */
225     public OnapCommand get(String cmdName) throws OnapCommandException {
226         return this.get(cmdName, this.getEnabledProductVersion());
227     }
228
229     /**
230      * Get the OnapCommand, which CLI main would use to find the command based on the command name.
231      *
232      * @param cmdName Name of command
233      * @param version product version
234      * @return OnapCommand
235      * @throws OnapCommandException Exception
236      */
237     public OnapCommand get(String cmdName, String version) throws OnapCommandException {
238         Class<? extends OnapCommand> cls = registry.get(cmdName + ":" + version);
239         //mrkanag: Restrict auth/catalog type commands only available during devMode. in production
240         //don't expose the auth type and catalog type commands
241
242         if (cls == null) {
243             throw new OnapCommandNotFound(cmdName, version);
244         }
245
246         OnapCommand cmd = OnapCommandDiscoveryUtils.loadCommandClass(cls);
247         String schemaName = OnapCommandDiscoveryUtils.getSchemaInfo(cmdName, version).getSchemaName();
248         cmd.initializeSchema(schemaName);
249
250         return cmd;
251     }
252
253     private Map<String, Class<OnapCommand>> autoDiscoverCommandPlugins() throws OnapCommandException {
254         List<Class<OnapCommand>> cmds = OnapCommandDiscoveryUtils.discoverCommandPlugins();
255         Map<String, Class<OnapCommand>> map = new HashMap<>();
256
257         for (Class<OnapCommand> cmd : cmds) {
258             if (cmd.isAnnotationPresent(OnapCommandSchema.class)) {
259                 OnapCommandSchema ano = cmd.getAnnotation(OnapCommandSchema.class);
260                 if (ano.schema() != null && !ano.schema().isEmpty()) {
261                     map.put(ano.schema(), cmd);
262                 } else if (ano.type() != null && !ano.type().isEmpty()) {
263                     this.registerProfilePlugin(ano.type(), cmd);
264                     map.put(ano.type(), cmd);
265                 } else {
266                     throw new OnapUnsupportedSchemaProfile(ano.schema());
267                 }
268             }
269         }
270
271         return map;
272     }
273
274     private void autoDiscoverSchemas() throws OnapCommandException {
275         List<OnapCommandSchemaInfo> schemas = OnapCommandDiscoveryUtils.discoverOrLoadSchemas(true);
276
277         Map<String, Class<OnapCommand>> plugins = this.autoDiscoverCommandPlugins();
278
279         for (OnapCommandSchemaInfo schema : schemas) {
280             if (schema.isIgnore()) {
281                 LOG.info("Ignoring schema " + schema.getSchemaURI());
282                 continue;
283             }
284
285             //First check if there is an specific plugin exist, otherwise check for profile plugin
286             if (plugins.containsKey(schema.getSchemaName())) {
287                 this.register(schema.getCmdName(), schema.getProduct(), plugins.get(schema.getSchemaName()));
288             } else if (plugins.containsKey(schema.getSchemaProfile())) {
289                 this.register(schema.getCmdName(), schema.getProduct(), plugins.get(schema.getSchemaProfile()));
290             } else {
291                 LOG.info("Ignoring schema " + schema.getSchemaURI());
292             }
293         }
294     }
295
296     /**
297      * Helps to find the Oclip CLI version, could be used with --version or -v option.
298      *
299      * @return string
300      */
301     public String getVersion() {
302         String version = this.getClass().getPackage().getImplementationVersion();
303         if (version == null) {
304             version = OnapCommandConfig.getPropertyValue(OnapCommandConstants.OPEN_CLI_VERSION);
305         }
306
307         String buildTime = OnapCommandHelperUtils.findLastBuildTime();
308         if (buildTime != null && !buildTime.isEmpty()) {
309             buildTime = " [" + buildTime + "]";
310         } else {
311             buildTime = "";
312         }
313
314         String configuredProductVersion = this.getEnabledProductVersion();
315
316         String versionInfo = "";
317         try {
318             versionInfo = IOUtils.toString(this.getClass().getClassLoader().getResourceAsStream(OnapCommandConstants.VERSION_INFO));
319         } catch (IOException e) { // NOSONAR
320             //Never occurs
321         }
322
323         versionInfo = versionInfo.replaceAll(OnapCommandConstants.VERSION_INFO_PLACE_HOLDER_ENB_PRD_VER, configuredProductVersion);
324         versionInfo = versionInfo.replaceAll(OnapCommandConstants.VERSION_INFO_PLACE_HOLDER_AVL_PRD_VER, this.availableProductVersions.toString());
325         versionInfo = versionInfo.replaceAll(OnapCommandConstants.VERSION_INFO_PLACE_HOLDER_VERSION + "", version + buildTime);
326
327         return versionInfo;
328     }
329
330     /**
331      * Provides the help message in tabular format for all commands registered in this registrar.
332      *
333      * @return string
334      * @throws OnapCommandHelpFailed Help cmd failed
335      */
336     public String getHelp() throws OnapCommandHelpFailed {
337         return this.getHelp(false);
338     }
339
340     public String getHelpForEnabledProductVersion() throws OnapCommandHelpFailed {
341         return this.getHelp(true);
342     }
343
344     private String getHelp(boolean isEnabledProductVersionOnly) throws OnapCommandHelpFailed {
345         OnapCommandResult help = new OnapCommandResult();
346         help.setType(OnapCommandResultType.TABLE);
347         help.setPrintDirection(OnapCommandPrintDirection.LANDSCAPE);
348
349         OnapCommandResultAttribute attr = new OnapCommandResultAttribute();
350         attr.setName(OnapCommandConstants.NAME.toUpperCase());
351         attr.setDescription(OnapCommandConstants.DESCRIPTION);
352         attr.setScope(OnapCommandResultAttributeScope.SHORT);
353         help.getRecords().add(attr);
354
355         OnapCommandResultAttribute attrVer = new OnapCommandResultAttribute();
356         if (!isEnabledProductVersionOnly) {
357             attrVer.setName(OnapCommandConstants.INFO_PRODUCT.toUpperCase());
358             attrVer.setDescription(OnapCommandConstants.DESCRIPTION);
359             attrVer.setScope(OnapCommandResultAttributeScope.SHORT);
360             help.getRecords().add(attrVer);
361         }
362
363         OnapCommandResultAttribute attrSrv = new OnapCommandResultAttribute();
364         attrSrv.setName(OnapCommandConstants.INFO_SERVICE.toUpperCase());
365         attrSrv.setDescription(OnapCommandConstants.INFO_SERVICE);
366         attrSrv.setScope(OnapCommandResultAttributeScope.SHORT);
367         help.getRecords().add(attrSrv);
368
369         OnapCommandResultAttribute attrDesc = new OnapCommandResultAttribute();
370         attrDesc.setName(OnapCommandConstants.DESCRIPTION.toUpperCase());
371         attrDesc.setDescription(OnapCommandConstants.DESCRIPTION);
372         attrDesc.setScope(OnapCommandResultAttributeScope.SHORT);
373         help.getRecords().add(attrDesc);
374
375         for (String cmdName : isEnabledProductVersionOnly ? OnapCommandUtils.sort(this.listCommandsForEnabledProductVersion()) : OnapCommandUtils.sort(this.listCommands())) {
376             OnapCommand cmd;
377             try {
378                 if (!isEnabledProductVersionOnly) {
379                     String[] cmdVer = cmdName.split(":");
380                     cmd = this.get(cmdVer[0], cmdVer[1]);
381                     attr.getValues().add(cmdVer[0]);
382                     attrVer.getValues().add(cmdVer[1]);
383                 } else {
384                     cmd = this.get(cmdName);
385                     attr.getValues().add(cmdName);
386                 }
387
388                 attrSrv.getValues().add(cmd.printVersion());
389                 attrDesc.getValues().add(cmd.getDescription());
390             } catch (OnapCommandException e) {
391                 throw new OnapCommandHelpFailed(e);
392             }
393         }
394
395         try {
396             return "\n\nCommands:\n" + help.print() + (isEnabledProductVersionOnly ? "" : "\n" + this.getVersion());
397         } catch (OnapCommandException e) {
398             throw new OnapCommandHelpFailed(e);
399         }
400     }
401
402     public List<Map<String, ?>> getTestSuite(String cmd) throws OnapCommandException {
403         return OnapCommandDiscoveryUtils.createTestSuite(cmd, enabledProductVersion);
404     }
405 }