fd6be82db5261c95323c00726be40ee3a40bc32a
[ccsdk/features.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  */
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.http.about;
23
24 import java.io.IOException;
25 import java.net.URL;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.jar.Attributes;
30 import java.util.jar.Manifest;
31 import javax.servlet.ServletException;
32 import javax.servlet.ServletOutputStream;
33 import javax.servlet.http.HttpServlet;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpServletResponse;
36 //import org.apache.karaf.bundle.core.BundleInfo;
37 //import org.apache.karaf.bundle.core.BundleService;
38 import org.onap.ccsdk.features.sdnr.wt.common.Resources;
39 import org.onap.ccsdk.features.sdnr.wt.common.file.PomFile;
40 import org.onap.ccsdk.features.sdnr.wt.common.file.PomPropertiesFile;
41 import org.osgi.framework.Bundle;
42 import org.osgi.framework.BundleContext;
43 import org.osgi.framework.FrameworkUtil;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 public class AboutHttpServlet extends HttpServlet {
48
49     /**
50      *
51      */
52     private static final long serialVersionUID = 1L;
53     private static final Logger LOG = LoggerFactory.getLogger(AboutHttpServlet.class);
54     private static final String UNKNOWN = "unknown";
55     private static final String METAINF_MAVEN = "/META-INF/maven/";
56     private static final String EXCEPTION_FORMAT_UNABLE_TO_READ_INNER_POMFILE = "unable to read inner pom file: {}";
57
58     private static final String URI_PRE = "/about";
59     private static final String RES_BASEPATH = "about/";
60
61     private static final String PLACEHOLDER_ONAP_RELEASENAME = "{release-name}";
62     private static final String PLACEHOLDER_ONAP_RELEASEVERSION = "{release-version}";
63     private static final String PLACEHOLDER_ODL_RELEASENAME = "{odl-version}";
64     private static final String PLACEHOLDER_BUILD_TIMESTAMP = "{build-time}";
65     private static final String PLACEHOLDER_PACKAGE_GITHASH = "{package-githash}";
66     private static final String PLACEHOLDER_PACAKGE_VERSION = "{package-version}";
67     private static final String PLACEHOLDER_CCSDK_VERSION = "{ccsdk-version}";
68     private static final String PLACEHOLDER_CLUSTER_SIZE = "{cluster-size}";
69     private static final String PLACEHOLDER_MDSAL_VERSION = "{mdsal-version}";
70     private static final String PLACEHOLDER_YANGTOOLS_VERSION = "{yangtools-version}";
71     private static final String PLACEHOLDER_KARAF_INFO = "{karaf-info}";
72     private static final String PLACEHOLDER_DEVICEMANAGER_TABLE = "{devicemanagers}";
73     private static final String README_FILE = "README.md";
74     private static final String NO_DEVICEMANAGERS_RUNNING_MESSAGE = null;
75
76     private final String groupId = this.getGroupIdOrDefault("org.onap.ccsdk.features.sdnr.wt");
77     private final String artifactId = "sdnr-wt-data-provider-provider";
78
79     private final Map<Integer,String> BUNDLESTATE_LUT;
80     private final Map<String, String> data;
81     private final String readmeContent;
82     //  private BundleService bundleService;
83
84
85     public AboutHttpServlet() {
86
87         this.data = new HashMap<>();
88         this.collectStaticData();
89         this.readmeContent = this.render(this.getResourceFileContent(README_FILE));
90         this.BUNDLESTATE_LUT = new HashMap<>();
91         this.BUNDLESTATE_LUT.put(Bundle.UNINSTALLED, "uninstalled");
92         this.BUNDLESTATE_LUT.put(Bundle.INSTALLED, "installed");
93         this.BUNDLESTATE_LUT.put(Bundle.RESOLVED, "resolved");
94         this.BUNDLESTATE_LUT.put(Bundle.STARTING, "starting");
95         this.BUNDLESTATE_LUT.put(Bundle.STOPPING, "stopping");
96         this.BUNDLESTATE_LUT.put(Bundle.ACTIVE, "active");
97
98     }
99
100     protected String getGroupIdOrDefault(String def) {
101                 String symbolicName = this.getManifestValue("Bundle-SymbolicName");
102                 if(symbolicName!=null) {
103                         int idx =  symbolicName.indexOf(this.artifactId);
104                         if(idx>0) {
105                                 return symbolicName.substring(0, idx-1);
106                         }
107                 }
108                 return def;
109         }
110
111         //      public void setBundleService(BundleService bundleService) {
112     //          this.bundleService = bundleService;
113     //  }
114
115     /**
116      * collect static versioning data
117      */
118     private void collectStaticData() {
119         PomPropertiesFile props = this.getPomProperties();
120         final String ccsdkVersion = this.getPomParentVersion();
121         final String mdsalVersion = SystemInfo.getMdSalVersion(UNKNOWN);
122         this.data.put(PLACEHOLDER_ONAP_RELEASENAME, ODLVersionLUT.getONAPReleaseName(ccsdkVersion, UNKNOWN));
123         this.data.put(PLACEHOLDER_ODL_RELEASENAME, ODLVersionLUT.getOdlVersion(mdsalVersion, UNKNOWN));
124         this.data.put(PLACEHOLDER_BUILD_TIMESTAMP, props != null ? props.getBuildDate().toString() : "");
125         this.data.put(PLACEHOLDER_PACAKGE_VERSION, this.getManifestValue("Bundle-Version"));
126         this.data.put(PLACEHOLDER_CCSDK_VERSION, ccsdkVersion);
127         this.data.put(PLACEHOLDER_ONAP_RELEASEVERSION, SystemInfo.getOnapVersion(UNKNOWN));
128         this.data.put(PLACEHOLDER_MDSAL_VERSION, mdsalVersion);
129         this.data.put(PLACEHOLDER_YANGTOOLS_VERSION, SystemInfo.getYangToolsVersion(UNKNOWN));
130         this.data.put(PLACEHOLDER_PACKAGE_GITHASH, this.getGitHash(UNKNOWN));
131     }
132
133     @Override
134     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
135
136         String uri = req.getRequestURI().substring(URI_PRE.length());
137         LOG.debug("request for {}", uri);
138         if (uri.length() <= 0 || uri.equals("/")) {
139             // collect data
140             this.collectData();
141             // render readme
142             String content = this.render();
143             byte[] output = content != null ? content.getBytes() : new byte[0];
144             // output
145             resp.setStatus(HttpServletResponse.SC_OK);
146             resp.setContentLength(output.length);
147             resp.setContentType("text/plain");
148             ServletOutputStream os = null;
149             try {
150                 os = resp.getOutputStream();
151                 os.write(output);
152             } catch (IOException e) {
153                 LOG.warn("problem writing response for {}: {}", uri, e);
154             } finally {
155                 if (os != null) {
156                     try {
157                         os.close();
158                     } catch (IOException e) {
159                         LOG.warn("problem closing response stream: {}", e);
160                     }
161                 }
162             }
163
164         } else {
165             this.doGetFile(uri, resp);
166         }
167     }
168
169     /**
170      * load git.commit.id from jar /META-INF/git.properties
171      *
172      * @param def
173      */
174     private String getGitHash(String def) {
175         String content = Resources.getFileContent(AboutHttpServlet.class, "/META-INF/git.properties");
176         if (content == null) {
177             return def;
178         }
179         String lines[] = content.split("\n");
180         for (String line : lines) {
181             if (line.startsWith("git.commit.id")) {
182                 def = line.substring("git.commit.id=".length());
183                 break;
184             }
185         }
186         return def;
187     }
188
189     private String getResourceFileContent(String filename) {
190         LOG.debug("try ti get content of {}", filename);
191         return Resources.getFileContent(AboutHttpServlet.class, RES_BASEPATH + filename);
192     }
193
194     /**
195      * collect dynamic data for about.md
196      */
197     private void collectData() {
198         LOG.info("collecting dynamic data");
199         try {
200             this.data.put(PLACEHOLDER_KARAF_INFO, SystemInfo.get());
201             this.data.put(PLACEHOLDER_DEVICEMANAGER_TABLE, this.getDevicemanagerBundles());
202         } catch (Exception e) {
203             LOG.warn("problem collecting system data: {}", e);
204         }
205     }
206
207     /**
208      * get value for key out of /META-INF/MANIFEST.MF
209      *
210      * @param key
211      * @return
212      */
213     protected String getManifestValue(String key) {
214         URL url = Resources.getUrlForRessource(AboutHttpServlet.class, "/META-INF/MANIFEST.MF");
215         if (url == null) {
216             return null;
217         }
218         Manifest manifest;
219         try {
220             manifest = new Manifest(url.openStream());
221             Attributes attr = manifest.getMainAttributes();
222             return attr.getValue(key);
223         } catch (IOException e) {
224             LOG.warn("problem reading manifest: {}", e);
225         }
226         return null;
227
228     }
229
230     /**
231      * get object representation of /META-INF/maven/groupId/artifactId/pom.properties
232      *
233      * @return
234      */
235     private PomPropertiesFile getPomProperties() {
236         URL url = Resources.getUrlForRessource(AboutHttpServlet.class,
237                 METAINF_MAVEN + groupId + "/" + artifactId + "/pom.properties");
238         PomPropertiesFile propfile;
239         if (url == null) {
240             return null;
241         }
242         try {
243             propfile = new PomPropertiesFile(url.openStream());
244             return propfile;
245         } catch (Exception e) {
246             LOG.warn(EXCEPTION_FORMAT_UNABLE_TO_READ_INNER_POMFILE, e);
247         }
248         return null;
249     }
250
251     /**
252      * get value for key out of /META-INF/maven/groupId/artifactId/pom.xml in properties section
253      *
254      * @param key
255      * @return
256      */
257     private String getPomProperty(String key) {
258         LOG.info("try to get pom property for {}", key);
259         URL url = Resources.getUrlForRessource(AboutHttpServlet.class,
260                 METAINF_MAVEN + groupId + "/" + artifactId + "/pom.xml");
261         if (url == null) {
262             return null;
263         }
264         PomFile pomfile;
265         try {
266             pomfile = new PomFile(url.openStream());
267             return pomfile.getProperty(key);
268         } catch (Exception e) {
269             LOG.warn(EXCEPTION_FORMAT_UNABLE_TO_READ_INNER_POMFILE, e);
270         }
271         return null;
272     }
273
274     /**
275      * get parent pom version out of /META-INF/maven/groupId/artifactId/pom.xml
276      *
277      * @return
278      */
279     private String getPomParentVersion() {
280         LOG.info("try to get pom parent version");
281         URL url = Resources.getUrlForRessource(AboutHttpServlet.class,
282                 METAINF_MAVEN + groupId + "/" + artifactId + "/pom.xml");
283         if (url == null) {
284             return null;
285         }
286         PomFile pomfile;
287         try {
288             pomfile = new PomFile(url.openStream());
289             return pomfile.getParentVersion();
290         } catch (Exception e) {
291             LOG.warn(EXCEPTION_FORMAT_UNABLE_TO_READ_INNER_POMFILE, e);
292         }
293         return null;
294     }
295
296     private String getDevicemanagerBundles() {
297         Bundle thisbundle = FrameworkUtil.getBundle(this.getClass());
298         BundleContext context = thisbundle ==null?null:thisbundle.getBundleContext();
299         if (context == null) {
300             LOG.debug("no bundle context available");
301             return "";
302         }
303         Bundle[] bundles = context.getBundles();
304         if (bundles == null || bundles.length <= 0) {
305             LOG.debug("no bundles found");
306             return NO_DEVICEMANAGERS_RUNNING_MESSAGE;
307         }
308         LOG.debug("found {} bundles", bundles.length);
309         MarkdownTable table = new MarkdownTable();
310         table.setHeader(new String[] {"Bundle-Id","Version","Symbolic-Name","Status"});
311         String name;
312         for (Bundle bundle : bundles) {
313             name = bundle.getSymbolicName();
314             if(!(name.contains("devicemanager") && name.contains("provider"))) {
315                 continue;
316             }
317             if(name.equals("org.onap.ccsdk.features.sdnr.wt.sdnr-wt-devicemanager-provider")) {
318                 continue;
319             }
320             table.addRow(new String[] {String.valueOf(bundle.getBundleId()), bundle.getVersion().toString(), name,
321                 BUNDLESTATE_LUT.getOrDefault(bundle.getState(),"unknown")});
322
323         }
324         return table.toMarkDown();
325     }
326
327     /**
328      * get file by uri from resources and write out to response stream
329      *
330      * @param uri
331      * @param resp
332      */
333     private void doGetFile(String uri, HttpServletResponse resp) {
334         String content = this.getResourceFileContent(uri);
335         if (content == null) {
336             try {
337                 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
338             } catch (IOException e) {
339                 LOG.debug("unable to send error response : {}", e);
340             }
341         } else {
342             byte[] data = content.getBytes();
343             resp.setStatus(HttpServletResponse.SC_OK);
344             resp.setContentType(this.getContentType(uri));
345             try {
346                 resp.getOutputStream().write(data);
347             } catch (IOException e) {
348                 LOG.debug("unable to send data : {}", e);
349             }
350         }
351
352     }
353
354     /**
355      * create http response contentType by filename
356      *
357      * @param filename
358      * @return
359      */
360     private String getContentType(String filename) {
361         String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
362         switch (ext) {
363             case "jpg":
364             case "jpeg":
365             case "svg":
366             case "png":
367             case "gif":
368             case "bmp":
369                 return "image/" + ext;
370             case "json":
371                 return "application/json";
372             case "html":
373             case "htm":
374                 return "text/html";
375             case "txt":
376             case "md":
377             default:
378                 return "text/plain";
379         }
380     }
381
382     /**
383      * render this.readmeContent with this.data
384      *
385      * @return
386      */
387     private String render() {
388         return this.render(null);
389     }
390
391     /**
392      * render content with this.data
393      *
394      * @param content
395      * @return
396      */
397     private String render(String content) {
398         if (content == null) {
399             content = this.readmeContent;
400         }
401         if (content == null) {
402             return null;
403         }
404         for (Entry<String, String> entry : this.data.entrySet()) {
405             if (entry.getValue() != null && content.contains(entry.getKey())) {
406                 content = content.replace(entry.getKey(), entry.getValue());
407             }
408         }
409
410         return content;
411     }
412
413     public void setClusterSize(String value) {
414         this.data.put(PLACEHOLDER_CLUSTER_SIZE, value);
415     }
416 }