2  * ============LICENSE_START=======================================================
 
   3  * ONAP : ccsdk features
 
   4  * ================================================================================
 
   5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property.
 
   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
 
  12  *     http://www.apache.org/licenses/LICENSE-2.0
 
  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=========================================================
 
  22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.http.about;
 
  24 import java.io.IOException;
 
  26 import java.util.Date;
 
  27 import java.util.HashMap;
 
  29 import java.util.Map.Entry;
 
  30 import java.util.jar.Attributes;
 
  31 import java.util.jar.Manifest;
 
  32 import javax.servlet.ServletException;
 
  33 import javax.servlet.ServletOutputStream;
 
  34 import javax.servlet.http.HttpServlet;
 
  35 import javax.servlet.http.HttpServletRequest;
 
  36 import javax.servlet.http.HttpServletResponse;
 
  37 import org.apache.http.HttpHeaders;
 
  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.dataprovider.model.types.NetconfTimeStampImpl;
 
  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;
 
  47 public class AboutHttpServlet extends HttpServlet {
 
  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: {}";
 
  58     private static final String URI_PRE = "/about";
 
  59     private static final String RES_BASEPATH = "about/";
 
  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 JSON_FILE = "README.json";
 
  75     private static final String NO_DEVICEMANAGERS_RUNNING_MESSAGE = null;
 
  76     private static final String MIMETYPE_JSON = "application/json";
 
  77     private static final String MIMETYPE_MARKDOWN = "text/markdown";
 
  79     private final String groupId = this.getGroupIdOrDefault("org.onap.ccsdk.features.sdnr.wt");
 
  80     private final String artifactId = "sdnr-wt-data-provider-provider";
 
  82     private final Map<Integer, String> BUNDLESTATE_LUT;
 
  83     private final Map<String, String> data;
 
  84     private final String readmeContent;
 
  85     private final String jsonContent;
 
  88     public AboutHttpServlet() {
 
  90         this.data = new HashMap<>();
 
  91         this.collectStaticData();
 
  92         this.readmeContent = this.render(ContentType.MARKDOWN, this.getResourceFileContent(README_FILE));
 
  93         this.jsonContent = this.render(ContentType.MARKDOWN, this.getResourceFileContent(JSON_FILE));
 
  94         this.BUNDLESTATE_LUT = new HashMap<>();
 
  95         this.BUNDLESTATE_LUT.put(Bundle.UNINSTALLED, "uninstalled");
 
  96         this.BUNDLESTATE_LUT.put(Bundle.INSTALLED, "installed");
 
  97         this.BUNDLESTATE_LUT.put(Bundle.RESOLVED, "resolved");
 
  98         this.BUNDLESTATE_LUT.put(Bundle.STARTING, "starting");
 
  99         this.BUNDLESTATE_LUT.put(Bundle.STOPPING, "stopping");
 
 100         this.BUNDLESTATE_LUT.put(Bundle.ACTIVE, "active");
 
 104     protected String getGroupIdOrDefault(String def) {
 
 105         String symbolicName = this.getManifestValue("Bundle-SymbolicName");
 
 106         if (symbolicName != null) {
 
 107             int idx = symbolicName.indexOf(this.artifactId);
 
 109                 return symbolicName.substring(0, idx - 1);
 
 116      * collect static versioning data
 
 118     private void collectStaticData() {
 
 119         final String ccsdkVersion = this.getPomParentVersion();
 
 120         final String mdsalVersion = SystemInfo.getMdSalVersion(UNKNOWN);
 
 121         this.data.put(PLACEHOLDER_ONAP_RELEASENAME, ODLVersionLUT.getONAPReleaseName(ccsdkVersion, UNKNOWN));
 
 122         this.data.put(PLACEHOLDER_ODL_RELEASENAME, ODLVersionLUT.getOdlVersion(mdsalVersion, UNKNOWN));
 
 123         this.data.put(PLACEHOLDER_BUILD_TIMESTAMP, getDate(this.getManifestValue("Bnd-LastModified"), UNKNOWN));
 
 124         this.data.put(PLACEHOLDER_PACAKGE_VERSION, this.getManifestValue("Bundle-Version"));
 
 125         this.data.put(PLACEHOLDER_CCSDK_VERSION, ccsdkVersion);
 
 126         this.data.put(PLACEHOLDER_ONAP_RELEASEVERSION, SystemInfo.getOnapVersion(UNKNOWN));
 
 127         this.data.put(PLACEHOLDER_MDSAL_VERSION, mdsalVersion);
 
 128         this.data.put(PLACEHOLDER_YANGTOOLS_VERSION, SystemInfo.getYangToolsVersion(UNKNOWN));
 
 129         this.data.put(PLACEHOLDER_PACKAGE_GITHASH, this.getGitHash(UNKNOWN));
 
 132     private String getDate(String value, String defaultValue) {
 
 137             long x = Long.parseLong(value);
 
 138             return NetconfTimeStampImpl.getConverter().getTimeStampAsNetconfString(new Date(x));
 
 140         catch(NumberFormatException e) {
 
 141             LOG.debug("date value is not a numeric one");
 
 147     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 
 149         String uri = req.getRequestURI().substring(URI_PRE.length());
 
 150         LOG.debug("request for {}", uri);
 
 151         if (uri.length() <= 0 || uri.equals("/")) {
 
 152             ContentType ctype = this.detectContentType(req, ContentType.MARKDOWN);
 
 154             this.collectData(ctype);
 
 156             String content = this.render(ctype);
 
 157             byte[] output = content != null ? content.getBytes() : new byte[0];
 
 159             resp.setStatus(HttpServletResponse.SC_OK);
 
 160             resp.setContentLength(output.length);
 
 161             resp.setContentType(ctype.getMimeType());
 
 162             try (ServletOutputStream os = resp.getOutputStream()) {
 
 164             } catch (IOException e) {
 
 165                 LOG.warn("problem writing response for {}: {}", uri, e);
 
 168             this.doGetFile(uri, resp);
 
 173      * load git.commit.id from jar /META-INF/git.properties
 
 177     private String getGitHash(String def) {
 
 178         String content = Resources.getFileContent(AboutHttpServlet.class, "/META-INF/git.properties");
 
 179         if (content == null) {
 
 182         String[] lines = content.split("\n");
 
 183         for (String line : lines) {
 
 184             if (line.startsWith("git.commit.id")) {
 
 185                 def = line.substring("git.commit.id=".length());
 
 192     private String getResourceFileContent(String filename) {
 
 193         LOG.debug("try ti get content of {}", filename);
 
 194         return Resources.getFileContent(AboutHttpServlet.class, RES_BASEPATH + filename);
 
 198      * collect dynamic data for about.md
 
 200     private void collectData(ContentType ctype) {
 
 201         LOG.info("collecting dynamic data with content-type {}", ctype);
 
 203             this.data.put(PLACEHOLDER_KARAF_INFO, SystemInfo.get());
 
 204             this.data.put(PLACEHOLDER_DEVICEMANAGER_TABLE, this.getDevicemanagerBundles(ctype));
 
 205         } catch (Exception e) {
 
 206             LOG.warn("problem collecting system data: ", e);
 
 211      * get value for key out of /META-INF/MANIFEST.MF
 
 216     protected String getManifestValue(String key) {
 
 217         URL url = Resources.getUrlForRessource(AboutHttpServlet.class, "/META-INF/MANIFEST.MF");
 
 223             manifest = new Manifest(url.openStream());
 
 224             Attributes attr = manifest.getMainAttributes();
 
 225             return attr.getValue(key);
 
 226         } catch (IOException e) {
 
 227             LOG.warn("problem reading manifest: ", e);
 
 234      * get parent pom version out of /META-INF/maven/groupId/artifactId/pom.xml
 
 238     private String getPomParentVersion() {
 
 239         LOG.info("try to get pom parent version");
 
 240         URL url = Resources.getUrlForRessource(AboutHttpServlet.class,
 
 241                 METAINF_MAVEN + groupId + "/" + artifactId + "/pom.xml");
 
 247             pomfile = new PomFile(url.openStream());
 
 248             return pomfile.getParentVersion();
 
 249         } catch (Exception e) {
 
 250             LOG.warn(EXCEPTION_FORMAT_UNABLE_TO_READ_INNER_POMFILE, e);
 
 255     private String getDevicemanagerBundles(ContentType ctype) {
 
 256         Bundle thisbundle = FrameworkUtil.getBundle(this.getClass());
 
 257         BundleContext context = thisbundle == null ? null : thisbundle.getBundleContext();
 
 258         if (context == null) {
 
 259             LOG.debug("no bundle context available");
 
 260             return ctype == ContentType.MARKDOWN ? "" : "[]";
 
 262         Bundle[] bundles = context.getBundles();
 
 263         if (bundles == null || bundles.length <= 0) {
 
 264             LOG.debug("no bundles found");
 
 265             return ctype == ContentType.MARKDOWN ? NO_DEVICEMANAGERS_RUNNING_MESSAGE : "[]";
 
 267         LOG.debug("found {} bundles", bundles.length);
 
 268         MarkdownTable table = new MarkdownTable();
 
 269         table.setHeader(new String[] {"Bundle-Id", "Version", "Symbolic-Name", "Status"});
 
 271         for (Bundle bundle : bundles) {
 
 272             name = bundle.getSymbolicName();
 
 273             if (!(name.contains("devicemanager") && name.contains("provider"))) {
 
 276             if (name.equals("org.onap.ccsdk.features.sdnr.wt.sdnr-wt-devicemanager-provider")) {
 
 279             table.addRow(new String[] {String.valueOf(bundle.getBundleId()), bundle.getVersion().toString(), name,
 
 280                     BUNDLESTATE_LUT.getOrDefault(bundle.getState(), UNKNOWN)});
 
 283         return ctype == ContentType.MARKDOWN ? table.toMarkDown() : table.toJson();
 
 287      * get file by uri from resources and write out to response stream
 
 292     private void doGetFile(String uri, HttpServletResponse resp) {
 
 293         String content = this.getResourceFileContent(uri);
 
 294         if (content == null) {
 
 296                 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
 
 297             } catch (IOException e) {
 
 298                 LOG.debug("unable to send error response : {}", e);
 
 301             byte[] data = content.getBytes();
 
 302             resp.setStatus(HttpServletResponse.SC_OK);
 
 303             resp.setContentType(this.getContentType(uri));
 
 305                 resp.getOutputStream().write(data);
 
 306             } catch (IOException e) {
 
 307                 LOG.debug("unable to send data: ", e);
 
 314      * create http response contentType by filename
 
 319     private String getContentType(String filename) {
 
 320         String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
 
 328                 return "image/" + ext;
 
 330                 return MIMETYPE_JSON;
 
 342      * render this.readmeContent with this.data
 
 348     private String render(ContentType ctype) {
 
 349         return this.render(ctype, null);
 
 353      * render content with this.data
 
 358     private String render(ContentType ctype, String content) {
 
 359         if (content == null) {
 
 360             content = ctype == ContentType.MARKDOWN ? this.readmeContent : this.jsonContent;
 
 362         if (content == null) {
 
 365         for (Entry<String, String> entry : this.data.entrySet()) {
 
 366             if (entry.getValue() != null && content.contains(entry.getKey())) {
 
 367                 content = content.replace(entry.getKey(), entry.getValue());
 
 374     public void setClusterSize(String value) {
 
 375         this.data.put(PLACEHOLDER_CLUSTER_SIZE, value);
 
 378     private ContentType detectContentType(HttpServletRequest req, ContentType def) {
 
 379         String accept = req.getHeader(HttpHeaders.ACCEPT);
 
 380         if (accept != null) {
 
 381             if (accept.equals(MIMETYPE_JSON)) {
 
 382                 return ContentType.JSON;
 
 383             } else if (accept.equals(MIMETYPE_MARKDOWN)) {
 
 384                 return ContentType.MARKDOWN;
 
 390     private enum ContentType {
 
 391         MARKDOWN(MIMETYPE_MARKDOWN), JSON(MIMETYPE_JSON);
 
 393         private String mimeType;
 
 395         ContentType(String mimeType) {
 
 396             this.mimeType = mimeType;
 
 399         String getMimeType() {
 
 400             return this.mimeType;