Add support for profile include exclude
[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 java.io.IOException;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 import org.apache.commons.io.IOUtils;
27 import org.onap.cli.fw.cmd.OnapCommand;
28 import org.onap.cli.fw.conf.OnapCommandConfig;
29 import org.onap.cli.fw.conf.OnapCommandConstants;
30 import org.onap.cli.fw.error.OnapCommandException;
31 import org.onap.cli.fw.error.OnapCommandHelpFailed;
32 import org.onap.cli.fw.error.OnapCommandInvalidRegistration;
33 import org.onap.cli.fw.error.OnapCommandNotFound;
34 import org.onap.cli.fw.error.OnapCommandProductVersionInvalid;
35 import org.onap.cli.fw.error.OnapCommandRegistrationProductInfoMissing;
36 import org.onap.cli.fw.error.OnapUnsupportedSchemaProfile;
37 import org.onap.cli.fw.input.cache.OnapCommandParameterCache;
38 import org.onap.cli.fw.output.OnapCommandPrintDirection;
39 import org.onap.cli.fw.output.OnapCommandResult;
40 import org.onap.cli.fw.output.OnapCommandResultAttribute;
41 import org.onap.cli.fw.output.OnapCommandResultAttributeScope;
42 import org.onap.cli.fw.output.OnapCommandResultType;
43 import org.onap.cli.fw.schema.OnapCommandSchema;
44 import org.onap.cli.fw.schema.OnapCommandSchemaInfo;
45 import org.onap.cli.fw.utils.OnapCommandDiscoveryUtils;
46 import org.onap.cli.fw.utils.OnapCommandHelperUtils;
47 import org.onap.cli.fw.utils.OnapCommandUtils;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
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
115      *            Command Name
116      * @param cmd
117      *            Command Class
118      * @throws OnapCommandInvalidRegistration
119      *             Invalid registration exception
120      * @throws OnapCommandRegistrationProductInfoMissing
121      */
122     private void register(String name, String version, Class<? extends OnapCommand> cmd) throws OnapCommandInvalidRegistration, OnapCommandRegistrationProductInfoMissing {
123         if (version == null || version.isEmpty()) {
124             throw new OnapCommandRegistrationProductInfoMissing(name);
125         }
126
127         this.registry.put(name + ":" + version, cmd);
128         this.availableProductVersions.add(version);
129
130     }
131
132     private void registerProfilePlugin(String profile, Class<? extends OnapCommand> cmd) {
133         this.registryProfilePlugins.put(profile, cmd);
134     }
135
136     private OnapCommandRegistrar() {
137         this.enabledProductVersion = System.getenv(OnapCommandConstants.OPEN_CLI_PRODUCT_IN_USE_ENV_NAME);
138         if (this.enabledProductVersion  == null) {
139             this.enabledProductVersion  = OnapCommandConfig.getPropertyValue(OnapCommandConstants.OPEN_CLI_PRODUCT_NAME);
140         }
141     }
142
143     /**
144      * Get global registrar.
145      *
146      * @throws OnapCommandException
147      *             exception
148      */
149     public static OnapCommandRegistrar getRegistrar() throws OnapCommandException {
150         if (registrar == null) {
151             registrar = new OnapCommandRegistrar();
152             registrar.autoDiscoverSchemas();
153         }
154
155         return registrar;
156     }
157
158     /**
159      * Get the list of discovered commands by registrar.
160      *
161      * @return set
162      */
163     public Set<String> listCommands() {
164         return this.registry.keySet();
165     }
166
167     /**
168      * Get the list of discovered commands for a given product version in registrar.
169      *
170      * @return set
171      */
172     public Set<String> listCommandsForEnabledProductVersion() {
173         String version = this.getEnabledProductVersion();
174
175         Set<String> cmds = new HashSet<>();
176         if (!this.availableProductVersions.contains(version)) {
177             return cmds;
178         }
179
180         for (String cmd: this.registry.keySet()) {
181             if (cmd.split(":")[1].equalsIgnoreCase(version)) {
182                 cmds.add(cmd.split(":")[0]);
183             }
184         }
185         return cmds;
186     }
187
188     public Class<? extends OnapCommand> getProfilePlugin(String profile) throws OnapUnsupportedSchemaProfile {
189         if (!this.registryProfilePlugins.containsKey(profile)) {
190             throw new OnapUnsupportedSchemaProfile(profile);
191         }
192
193         return this.registryProfilePlugins.get(profile);
194     }
195
196     public Set<String> getAvailableProductVersions() {
197         return this.availableProductVersions;
198     }
199
200     public void setEnabledProductVersion(String version) throws OnapCommandProductVersionInvalid {
201         if (!this.availableProductVersions.contains(version)) {
202             throw new OnapCommandProductVersionInvalid(version, availableProductVersions);
203         }
204
205         this.enabledProductVersion = version;
206     }
207
208     public String getEnabledProductVersion() {
209         return this.enabledProductVersion;
210     }
211
212     /**
213      * Returns command details.
214      *
215      * @return map
216      * @throws OnapCommandException
217      *             exception
218      */
219     public List<OnapCommandSchemaInfo> listCommandInfo() throws OnapCommandException {
220         return OnapCommandDiscoveryUtils.discoverSchemas();
221     }
222
223     /**
224      * Get the OnapCommand, which CLI main would use to find the command based on the command name.
225      *
226      * @param cmdName
227      *            Name of command
228      * @return OnapCommand
229      * @throws OnapCommandException
230      *             Exception
231      */
232     public OnapCommand get(String cmdName) throws OnapCommandException {
233         return this.get(cmdName, this.getEnabledProductVersion());
234     }
235
236     /**
237      * Get the OnapCommand, which CLI main would use to find the command based on the command name.
238      *
239      * @param cmdName
240      *            Name of command
241      * @param version
242      *            product version
243      * @return OnapCommand
244      * @throws OnapCommandException
245      *             Exception
246      */
247     public OnapCommand get(String cmdName, String version) throws OnapCommandException {
248         Class<? extends OnapCommand> cls = registry.get(cmdName + ":" + version);
249         //mrkanag: Restrict auth/catalog type commands only available during devMode. in production
250         //don't expose the auth type and catalog type commands
251
252         if (cls == null) {
253             throw new OnapCommandNotFound(cmdName, version);
254         }
255
256         OnapCommand cmd = OnapCommandDiscoveryUtils.loadCommandClass(cls);
257         String schemaName = OnapCommandDiscoveryUtils.getSchemaInfo(cmdName, version).getSchemaName();
258         cmd.initializeSchema(schemaName);
259
260         return cmd;
261     }
262
263     private Map<String, Class<OnapCommand>> autoDiscoverCommandPlugins() throws OnapCommandException {
264         List<Class<OnapCommand>> cmds = OnapCommandDiscoveryUtils.discoverCommandPlugins();
265         Map<String, Class<OnapCommand>> map = new HashMap<>();
266
267         for (Class<OnapCommand> cmd : cmds) {
268             if (cmd.isAnnotationPresent(OnapCommandSchema.class)) {
269                 OnapCommandSchema ano = cmd.getAnnotation(OnapCommandSchema.class);
270                 if (ano.schema() != null && !ano.schema().isEmpty()) {
271                     map.put(ano.schema(), cmd);
272                 } else if (ano.type() != null && !ano.type().isEmpty()) {
273                     this.registerProfilePlugin(ano.type(), cmd);
274                     map.put(ano.type(), cmd);
275                 } else {
276                     throw new OnapUnsupportedSchemaProfile(ano.schema());
277                 }
278             }
279         }
280
281         return map;
282     }
283
284     private void autoDiscoverSchemas() throws OnapCommandException {
285         List<OnapCommandSchemaInfo> schemas = OnapCommandDiscoveryUtils.discoverOrLoadSchemas(true);
286
287         Map<String, Class<OnapCommand>> plugins = this.autoDiscoverCommandPlugins();
288
289         for (OnapCommandSchemaInfo schema : schemas) {
290             if (schema.isIgnore()) {
291                 LOG.info("Ignoring schema " + schema.getSchemaURI());
292                 continue;
293             }
294
295             //First check if there is an specific plugin exist, otherwise check for profile plugin
296              if (plugins.containsKey(schema.getSchemaName())) {
297                  this.register(schema.getCmdName(), schema.getProduct(), plugins.get(schema.getSchemaName()));
298              } else if (plugins.containsKey(schema.getSchemaProfile())) {
299                 this.register(schema.getCmdName(), schema.getProduct(), plugins.get(schema.getSchemaProfile()));
300             } else {
301                 LOG.info("Ignoring schema " + schema.getSchemaURI());
302             }
303         }
304     }
305
306     /**
307      * Helps to find the Oclip CLI version, could be used with --version or -v option.
308      *
309      * @return string
310      */
311     public String getVersion() {
312         String version = this.getClass().getPackage().getImplementationVersion();
313         if (version == null) {
314             version = OnapCommandConfig.getPropertyValue(OnapCommandConstants.OPEN_CLI_VERSION);
315         }
316
317         String buildTime = OnapCommandHelperUtils.findLastBuildTime();
318         if (buildTime!= null && !buildTime.isEmpty()) {
319             buildTime = " [" + buildTime + "]";
320         } else {
321             buildTime = "";
322         }
323
324         String configuredProductVersion = this.getEnabledProductVersion();
325
326         String versionInfo = "";
327         try {
328             versionInfo = IOUtils.toString(this.getClass().getClassLoader().getResourceAsStream(OnapCommandConstants.VERSION_INFO));
329         } catch (IOException e) { // NOSONAR
330             //Never occurs
331         }
332
333         versionInfo = versionInfo.replaceAll(OnapCommandConstants.VERSION_INFO_PLACE_HOLDER_ENB_PRD_VER, configuredProductVersion);
334         versionInfo = versionInfo.replaceAll(OnapCommandConstants.VERSION_INFO_PLACE_HOLDER_AVL_PRD_VER, this.availableProductVersions.toString());
335         versionInfo = versionInfo.replaceAll(OnapCommandConstants.VERSION_INFO_PLACE_HOLDER_VERSION + "", version + buildTime);
336
337         return versionInfo;
338     }
339
340     /**
341      * Provides the help message in tabular format for all commands registered in this registrar.
342      *
343      * @return string
344      * @throws OnapCommandHelpFailed
345      *             Help cmd failed
346      */
347     public String getHelp() throws OnapCommandHelpFailed {
348         return this.getHelp(false);
349     }
350
351     public String getHelpForEnabledProductVersion() throws OnapCommandHelpFailed {
352         return this.getHelp(true);
353     }
354
355     private String getHelp(boolean isEnabledProductVersionOnly) throws OnapCommandHelpFailed {
356         OnapCommandResult help = new OnapCommandResult();
357         help.setType(OnapCommandResultType.TABLE);
358         help.setPrintDirection(OnapCommandPrintDirection.LANDSCAPE);
359
360         OnapCommandResultAttribute attr = new OnapCommandResultAttribute();
361         attr.setName(OnapCommandConstants.NAME.toUpperCase());
362         attr.setDescription(OnapCommandConstants.DESCRIPTION);
363         attr.setScope(OnapCommandResultAttributeScope.SHORT);
364         help.getRecords().add(attr);
365
366         OnapCommandResultAttribute attrVer = new OnapCommandResultAttribute();
367         if (!isEnabledProductVersionOnly) {
368             attrVer.setName(OnapCommandConstants.INFO_PRODUCT.toUpperCase());
369             attrVer.setDescription(OnapCommandConstants.DESCRIPTION);
370             attrVer.setScope(OnapCommandResultAttributeScope.SHORT);
371             help.getRecords().add(attrVer);
372         }
373
374         OnapCommandResultAttribute attrSrv = new OnapCommandResultAttribute();
375         attrSrv.setName(OnapCommandConstants.INFO_SERVICE.toUpperCase());
376         attrSrv.setDescription(OnapCommandConstants.INFO_SERVICE);
377         attrSrv.setScope(OnapCommandResultAttributeScope.SHORT);
378         help.getRecords().add(attrSrv);
379
380         OnapCommandResultAttribute attrDesc = new OnapCommandResultAttribute();
381         attrDesc.setName(OnapCommandConstants.DESCRIPTION.toUpperCase());
382         attrDesc.setDescription(OnapCommandConstants.DESCRIPTION);
383         attrDesc.setScope(OnapCommandResultAttributeScope.SHORT);
384         help.getRecords().add(attrDesc);
385
386         for (String cmdName : isEnabledProductVersionOnly ? OnapCommandUtils.sort(this.listCommandsForEnabledProductVersion()) : OnapCommandUtils.sort(this.listCommands())) {
387             OnapCommand cmd;
388             try {
389                 if (!isEnabledProductVersionOnly) {
390                     String []cmdVer = cmdName.split(":");
391                     cmd = this.get(cmdVer[0], cmdVer[1]);
392                     attr.getValues().add(cmdVer[0]);
393                     attrVer.getValues().add(cmdVer[1]);
394                 } else {
395                     cmd = this.get(cmdName);
396                     attr.getValues().add(cmdName);
397                 }
398
399                 attrSrv.getValues().add(cmd.printVersion());
400                 attrDesc.getValues().add(cmd.getDescription());
401             } catch (OnapCommandException e) {
402                 throw new OnapCommandHelpFailed(e);
403             }
404         }
405
406         try {
407             return "\n\nCommands:\n" + help.print() + (isEnabledProductVersionOnly ? "" : "\n" + this.getVersion());
408         } catch (OnapCommandException e) {
409             throw new OnapCommandHelpFailed(e);
410         }
411     }
412 }