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.Servlet;
33 import javax.servlet.ServletException;
34 import javax.servlet.ServletOutputStream;
35 import javax.servlet.http.HttpServlet;
36 import javax.servlet.http.HttpServletRequest;
37 import javax.servlet.http.HttpServletResponse;
38 import org.apache.http.HttpHeaders;
39 import org.onap.ccsdk.features.sdnr.wt.common.Resources;
40 import org.onap.ccsdk.features.sdnr.wt.common.file.PomFile;
41 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.types.NetconfTimeStampImpl;
42 import org.osgi.framework.Bundle;
43 import org.osgi.framework.BundleContext;
44 import org.osgi.framework.FrameworkUtil;
45 import org.osgi.service.component.annotations.Component;
46 import org.osgi.service.component.annotations.ServiceScope;
47 import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletName;
48 import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletPattern;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
52 @HttpWhiteboardServletPattern("/about")
53 @HttpWhiteboardServletName("AboutHttpServlet")
54 @Component(service = Servlet.class)
55 public class AboutHttpServlet extends HttpServlet {
60 private static final long serialVersionUID = 1L;
61 private static final Logger LOG = LoggerFactory.getLogger(AboutHttpServlet.class);
62 private static final String UNKNOWN = "unknown";
63 private static final String METAINF_MAVEN = "/META-INF/maven/";
64 private static final String EXCEPTION_FORMAT_UNABLE_TO_READ_INNER_POMFILE = "unable to read inner pom file: {}";
66 private static final String URI_PRE = "/about";
67 private static final String RES_BASEPATH = "about/";
69 private static final String PLACEHOLDER_ONAP_RELEASENAME = "{release-name}";
70 private static final String PLACEHOLDER_ONAP_RELEASEVERSION = "{release-version}";
71 private static final String PLACEHOLDER_ODL_RELEASENAME = "{odl-version}";
72 private static final String PLACEHOLDER_BUILD_TIMESTAMP = "{build-time}";
73 private static final String PLACEHOLDER_PACKAGE_GITHASH = "{package-githash}";
74 private static final String PLACEHOLDER_PACAKGE_VERSION = "{package-version}";
75 private static final String PLACEHOLDER_CCSDK_VERSION = "{ccsdk-version}";
76 private static final String PLACEHOLDER_CLUSTER_SIZE = "{cluster-size}";
77 private static final String PLACEHOLDER_MDSAL_VERSION = "{mdsal-version}";
78 private static final String PLACEHOLDER_YANGTOOLS_VERSION = "{yangtools-version}";
79 private static final String PLACEHOLDER_KARAF_INFO = "{karaf-info}";
80 private static final String PLACEHOLDER_DEVICEMANAGER_TABLE = "{devicemanagers}";
81 private static final String README_FILE = "README.md";
82 private static final String JSON_FILE = "README.json";
83 private static final String NO_DEVICEMANAGERS_RUNNING_MESSAGE = null;
84 private static final String MIMETYPE_JSON = "application/json";
85 private static final String MIMETYPE_MARKDOWN = "text/markdown";
87 private final String groupId = this.getGroupIdOrDefault("org.onap.ccsdk.features.sdnr.wt");
88 private final String artifactId = "sdnr-wt-data-provider-provider";
90 private final Map<Integer, String> BUNDLESTATE_LUT;
91 private final Map<String, String> data;
92 private final String readmeContent;
93 private final String jsonContent;
96 public AboutHttpServlet() {
98 this.data = new HashMap<>();
99 this.collectStaticData();
100 this.readmeContent = this.render(ContentType.MARKDOWN, this.getResourceFileContent(README_FILE));
101 this.jsonContent = this.render(ContentType.MARKDOWN, this.getResourceFileContent(JSON_FILE));
102 this.BUNDLESTATE_LUT = new HashMap<>();
103 this.BUNDLESTATE_LUT.put(Bundle.UNINSTALLED, "uninstalled");
104 this.BUNDLESTATE_LUT.put(Bundle.INSTALLED, "installed");
105 this.BUNDLESTATE_LUT.put(Bundle.RESOLVED, "resolved");
106 this.BUNDLESTATE_LUT.put(Bundle.STARTING, "starting");
107 this.BUNDLESTATE_LUT.put(Bundle.STOPPING, "stopping");
108 this.BUNDLESTATE_LUT.put(Bundle.ACTIVE, "active");
111 protected String getGroupIdOrDefault(String def) {
112 String symbolicName = this.getManifestValue("Bundle-SymbolicName");
113 if (symbolicName != null) {
114 int idx = symbolicName.indexOf(this.artifactId);
116 return symbolicName.substring(0, idx - 1);
123 * collect static versioning data
125 private void collectStaticData() {
126 final String ccsdkVersion = this.getPomParentVersion();
127 final String mdsalVersion = SystemInfo.getMdSalVersion(UNKNOWN);
128 this.data.put(PLACEHOLDER_ONAP_RELEASENAME, ODLVersionLUT.getONAPReleaseName(ccsdkVersion, UNKNOWN));
129 this.data.put(PLACEHOLDER_ODL_RELEASENAME, ODLVersionLUT.getOdlVersion(mdsalVersion, UNKNOWN));
130 this.data.put(PLACEHOLDER_BUILD_TIMESTAMP, getDate(this.getManifestValue("Bnd-LastModified"), UNKNOWN));
131 this.data.put(PLACEHOLDER_PACAKGE_VERSION, this.getManifestValue("Bundle-Version"));
132 this.data.put(PLACEHOLDER_CCSDK_VERSION, ccsdkVersion);
133 this.data.put(PLACEHOLDER_ONAP_RELEASEVERSION, SystemInfo.getOnapVersion(UNKNOWN));
134 this.data.put(PLACEHOLDER_MDSAL_VERSION, mdsalVersion);
135 this.data.put(PLACEHOLDER_YANGTOOLS_VERSION, SystemInfo.getYangToolsVersion(UNKNOWN));
136 this.data.put(PLACEHOLDER_PACKAGE_GITHASH, this.getGitHash(UNKNOWN));
139 private String getDate(String value, String defaultValue) {
144 long x = Long.parseLong(value);
145 return NetconfTimeStampImpl.getConverter().getTimeStampAsNetconfString(new Date(x));
147 catch(NumberFormatException e) {
148 LOG.debug("date value is not a numeric one");
154 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
156 String uri = req.getRequestURI().substring(URI_PRE.length());
157 LOG.debug("request for {}", uri);
158 if (uri.length() <= 0 || uri.equals("/")) {
159 ContentType ctype = this.detectContentType(req, ContentType.MARKDOWN);
161 this.collectData(ctype);
163 String content = this.render(ctype);
164 byte[] output = content != null ? content.getBytes() : new byte[0];
166 resp.setStatus(HttpServletResponse.SC_OK);
167 resp.setContentLength(output.length);
168 resp.setContentType(ctype.getMimeType());
169 try (ServletOutputStream os = resp.getOutputStream()) {
171 } catch (IOException e) {
172 LOG.warn("problem writing response for {}: {}", uri, e);
175 this.doGetFile(uri, resp);
180 * load git.commit.id from jar /META-INF/git.properties
184 private String getGitHash(String def) {
185 String content = Resources.getFileContent(AboutHttpServlet.class, "/META-INF/git.properties");
186 if (content == null) {
189 String[] lines = content.split("\n");
190 for (String line : lines) {
191 if (line.startsWith("git.commit.id")) {
192 def = line.substring("git.commit.id=".length());
199 private String getResourceFileContent(String filename) {
200 LOG.debug("try ti get content of {}", filename);
201 return Resources.getFileContent(AboutHttpServlet.class, RES_BASEPATH + filename);
205 * collect dynamic data for about.md
207 private void collectData(ContentType ctype) {
208 LOG.info("collecting dynamic data with content-type {}", ctype);
210 this.data.put(PLACEHOLDER_KARAF_INFO, SystemInfo.get());
211 this.data.put(PLACEHOLDER_DEVICEMANAGER_TABLE, this.getDevicemanagerBundles(ctype));
212 } catch (Exception e) {
213 LOG.warn("problem collecting system data: ", e);
218 * get value for key out of /META-INF/MANIFEST.MF
223 protected String getManifestValue(String key) {
224 URL url = Resources.getUrlForRessource(AboutHttpServlet.class, "/META-INF/MANIFEST.MF");
230 manifest = new Manifest(url.openStream());
231 Attributes attr = manifest.getMainAttributes();
232 return attr.getValue(key);
233 } catch (IOException e) {
234 LOG.warn("problem reading manifest: ", e);
241 * get parent pom version out of /META-INF/maven/groupId/artifactId/pom.xml
245 private String getPomParentVersion() {
246 LOG.info("try to get pom parent version");
247 URL url = Resources.getUrlForRessource(AboutHttpServlet.class,
248 METAINF_MAVEN + groupId + "/" + artifactId + "/pom.xml");
254 pomfile = new PomFile(url.openStream());
255 return pomfile.getParentVersion();
256 } catch (Exception e) {
257 LOG.warn(EXCEPTION_FORMAT_UNABLE_TO_READ_INNER_POMFILE, e);
262 private String getDevicemanagerBundles(ContentType ctype) {
263 Bundle thisbundle = FrameworkUtil.getBundle(this.getClass());
264 BundleContext context = thisbundle == null ? null : thisbundle.getBundleContext();
265 if (context == null) {
266 LOG.debug("no bundle context available");
267 return ctype == ContentType.MARKDOWN ? "" : "[]";
269 Bundle[] bundles = context.getBundles();
270 if (bundles == null || bundles.length <= 0) {
271 LOG.debug("no bundles found");
272 return ctype == ContentType.MARKDOWN ? NO_DEVICEMANAGERS_RUNNING_MESSAGE : "[]";
274 LOG.debug("found {} bundles", bundles.length);
275 MarkdownTable table = new MarkdownTable();
276 table.setHeader(new String[] {"Bundle-Id", "Version", "Symbolic-Name", "Status"});
278 for (Bundle bundle : bundles) {
279 name = bundle.getSymbolicName();
280 if (!(name.contains("devicemanager") && name.contains("provider"))) {
283 if (name.equals("org.onap.ccsdk.features.sdnr.wt.sdnr-wt-devicemanager-provider")) {
286 table.addRow(new String[] {String.valueOf(bundle.getBundleId()), bundle.getVersion().toString(), name,
287 BUNDLESTATE_LUT.getOrDefault(bundle.getState(), UNKNOWN)});
290 return ctype == ContentType.MARKDOWN ? table.toMarkDown() : table.toJson();
294 * get file by uri from resources and write out to response stream
299 private void doGetFile(String uri, HttpServletResponse resp) {
300 String content = this.getResourceFileContent(uri);
301 if (content == null) {
303 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
304 } catch (IOException e) {
305 LOG.debug("unable to send error response : {}", e);
308 byte[] data = content.getBytes();
309 resp.setStatus(HttpServletResponse.SC_OK);
310 resp.setContentType(this.getContentType(uri));
312 resp.getOutputStream().write(data);
313 } catch (IOException e) {
314 LOG.debug("unable to send data: ", e);
321 * create http response contentType by filename
326 private String getContentType(String filename) {
327 String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
335 return "image/" + ext;
337 return MIMETYPE_JSON;
349 * render this.readmeContent with this.data
355 private String render(ContentType ctype) {
356 return this.render(ctype, null);
360 * render content with this.data
365 private String render(ContentType ctype, String content) {
366 if (content == null) {
367 content = ctype == ContentType.MARKDOWN ? this.readmeContent : this.jsonContent;
369 if (content == null) {
372 for (Entry<String, String> entry : this.data.entrySet()) {
373 if (entry.getValue() != null && content.contains(entry.getKey())) {
374 content = content.replace(entry.getKey(), entry.getValue());
381 public void setClusterSize(String value) {
382 this.data.put(PLACEHOLDER_CLUSTER_SIZE, value);
385 private ContentType detectContentType(HttpServletRequest req, ContentType def) {
386 String accept = req.getHeader(HttpHeaders.ACCEPT);
387 if (accept != null) {
388 if (accept.equals(MIMETYPE_JSON)) {
389 return ContentType.JSON;
390 } else if (accept.equals(MIMETYPE_MARKDOWN)) {
391 return ContentType.MARKDOWN;
397 private enum ContentType {
398 MARKDOWN(MIMETYPE_MARKDOWN), JSON(MIMETYPE_JSON);
400 private String mimeType;
402 ContentType(String mimeType) {
403 this.mimeType = mimeType;
406 String getMimeType() {
407 return this.mimeType;