[rrd4j] Upgrade rrd4j, improve exception handling (#11355)

* Upgrades rrd4j to 3.8
* Fixes deprecations
* Updates RRD4jChartServlet for ChartProvider API changes
* Improves RRD4jChartServlet exception handling

For the rrd4j changelog see:

https://raw.githubusercontent.com/rrd4j/rrd4j/master/changelog.txt

Related to openhab/openhab-core#2502

Signed-off-by: Wouter Born <github@maindrain.net>
This commit is contained in:
Wouter Born 2021-10-09 21:56:09 +02:00 committed by GitHub
parent dd432270e8
commit 2b5431df00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 75 deletions

View File

@ -23,7 +23,7 @@
<dependency>
<groupId>org.rrd4j</groupId>
<artifactId>rrd4j</artifactId>
<version>3.3.1</version>
<version>3.8</version>
</dependency>
</dependencies>
</project>

View File

@ -296,7 +296,7 @@ public class RRD4jPersistenceService implements QueryablePersistenceService {
for (double value : result.getValues(DATASOURCE_STATE)) {
if (!Double.isNaN(value) && (((ts >= start) && (ts <= end)) || (start == end))) {
RRD4jItem rrd4jItem = new RRD4jItem(itemName, mapToState(value, item, unit),
ZonedDateTime.ofInstant(Instant.ofEpochMilli(ts * 1000), ZoneId.systemDefault()));
ZonedDateTime.ofInstant(Instant.ofEpochSecond(ts), ZoneId.systemDefault()));
items.add(rrd4jItem);
}
ts += step;
@ -319,7 +319,7 @@ public class RRD4jPersistenceService implements QueryablePersistenceService {
try {
if (file.exists()) {
// recreate the RrdDb instance from the file
db = new RrdDb(file.getAbsolutePath());
db = RrdDb.of(file.getAbsolutePath());
} else {
File folder = new File(DB_FOLDER);
if (!folder.exists()) {
@ -328,7 +328,7 @@ public class RRD4jPersistenceService implements QueryablePersistenceService {
RrdDef rrdDef = getRrdDef(alias, file);
if (rrdDef != null) {
// create a new database file
db = new RrdDb(rrdDef);
db = RrdDb.of(rrdDef);
} else {
logger.debug(
"Did not create rrd4j database for item '{}' since no rrd definition could be determined. This is likely due to an unsupported item type.",
@ -349,7 +349,7 @@ public class RRD4jPersistenceService implements QueryablePersistenceService {
for (Map.Entry<String, RrdDefConfig> e : rrdDefs.entrySet()) {
// try to find special config
RrdDefConfig rdc = e.getValue();
if (rdc != null && rdc.appliesTo(itemName)) {
if (rdc.appliesTo(itemName)) {
useRdc = rdc;
break;
}
@ -543,13 +543,11 @@ public class RRD4jPersistenceService implements QueryablePersistenceService {
}
for (RrdDefConfig rrdDef : rrdDefs.values()) {
if (rrdDef != null) {
if (rrdDef.isValid()) {
logger.debug("Created {}", rrdDef);
} else {
logger.info("Removing invalid definition {}", rrdDef);
rrdDefs.remove(rrdDef.name);
}
if (rrdDef.isValid()) {
logger.debug("Created {}", rrdDef);
} else {
logger.info("Removing invalid definition {}", rrdDef);
rrdDefs.remove(rrdDef.name);
}
}
}

View File

@ -12,16 +12,18 @@
*/
package org.openhab.persistence.rrd4j.internal.charts;
import static java.util.Map.entry;
import java.awt.Color;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.io.UncheckedIOException;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.Hashtable;
import java.util.Map;
import java.util.Objects;
import javax.imageio.ImageIO;
import javax.servlet.Servlet;
@ -30,6 +32,9 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.items.GroupItem;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemNotFoundException;
@ -46,6 +51,7 @@ import org.osgi.service.http.NamespaceException;
import org.rrd4j.ConsolFun;
import org.rrd4j.core.RrdDb;
import org.rrd4j.graph.RrdGraph;
import org.rrd4j.graph.RrdGraphConstants.FontTag;
import org.rrd4j.graph.RrdGraphDef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -66,11 +72,15 @@ import org.slf4j.LoggerFactory;
* @author Jan N. Klug - a few improvements
*
*/
@NonNullByDefault
@Component(service = ChartProvider.class)
public class RRD4jChartServlet implements Servlet, ChartProvider {
private final Logger logger = LoggerFactory.getLogger(RRD4jChartServlet.class);
private static final int DEFAULT_HEIGHT = 240;
private static final int DEFAULT_WIDTH = 480;
/** the URI of this servlet */
public static final String SERVLET_NAME = "/rrdchart.png";
@ -81,29 +91,30 @@ public class RRD4jChartServlet implements Servlet, ChartProvider {
new Color(0, 255, 255, 30), new Color(255, 0, 128, 30), new Color(255, 128, 128, 30),
new Color(255, 255, 0, 30) };
protected static final Map<String, Long> PERIODS = new HashMap<>();
private static final Duration DEFAULT_PERIOD = Duration.ofDays(1);
static {
PERIODS.put("h", -3600000L);
PERIODS.put("4h", -14400000L);
PERIODS.put("8h", -28800000L);
PERIODS.put("12h", -43200000L);
PERIODS.put("D", -86400000L);
PERIODS.put("3D", -259200000L);
PERIODS.put("W", -604800000L);
PERIODS.put("2W", -1209600000L);
PERIODS.put("M", -2592000000L);
PERIODS.put("2M", -5184000000L);
PERIODS.put("4M", -10368000000L);
PERIODS.put("Y", -31536000000L);
private static final Map<String, Duration> PERIODS = Map.ofEntries( //
entry("h", Duration.ofHours(1)), entry("4h", Duration.ofHours(4)), //
entry("8h", Duration.ofHours(8)), entry("12h", Duration.ofHours(12)), //
entry("D", Duration.ofDays(1)), entry("2D", Duration.ofDays(2)), //
entry("3D", Duration.ofDays(3)), entry("W", Duration.ofDays(7)), //
entry("2W", Duration.ofDays(14)), entry("M", Duration.ofDays(30)), //
entry("2M", Duration.ofDays(60)), entry("4M", Duration.ofDays(120)), //
entry("Y", Duration.ofDays(365))//
);
private final HttpService httpService;
private final ItemUIRegistry itemUIRegistry;
private final TimeZoneProvider timeZoneProvider;
@Activate
public RRD4jChartServlet(final @Reference HttpService httpService, final @Reference ItemUIRegistry itemUIRegistry,
final @Reference TimeZoneProvider timeZoneProvider) {
this.httpService = httpService;
this.itemUIRegistry = itemUIRegistry;
this.timeZoneProvider = timeZoneProvider;
}
@Reference
protected HttpService httpService;
@Reference
protected ItemUIRegistry itemUIRegistry;
@Activate
protected void activate() {
try {
@ -125,35 +136,39 @@ public class RRD4jChartServlet implements Servlet, ChartProvider {
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
logger.debug("RRD4J received incoming chart request: {}", req);
int width = 480;
try {
width = Integer.parseInt(Objects.requireNonNull(req.getParameter("w")));
} catch (Exception e) {
}
int height = 240;
try {
height = Integer.parseInt(Objects.requireNonNull(req.getParameter("h")));
} catch (Exception e) {
}
Long period = PERIODS.get(req.getParameter("period"));
if (period == null) {
// use a day as the default period
period = PERIODS.get("D");
}
// Create the start and stop time
Date timeEnd = new Date();
Date timeBegin = new Date(timeEnd.getTime() + period);
int width = parseInt(req.getParameter("w"), DEFAULT_WIDTH);
int height = parseInt(req.getParameter("h"), DEFAULT_HEIGHT);
String periodParam = req.getParameter("period");
Duration period = periodParam == null ? DEFAULT_PERIOD : PERIODS.getOrDefault(periodParam, DEFAULT_PERIOD);
// Create the start and stop time
ZonedDateTime timeEnd = ZonedDateTime.now(timeZoneProvider.getTimeZone());
ZonedDateTime timeBegin = timeEnd.minus(period);
// Set the content type to that provided by the chart provider
res.setContentType("image/" + getChartType());
try {
BufferedImage chart = createChart(null, null, timeBegin, timeEnd, height, width, req.getParameter("items"),
req.getParameter("groups"), null, null);
// Set the content type to that provided by the chart provider
res.setContentType("image/" + getChartType());
ImageIO.write(chart, getChartType().toString(), res.getOutputStream());
} catch (ItemNotFoundException e) {
logger.debug("Item not found error while generating chart.");
logger.debug("Item not found error while generating chart", e);
throw new ServletException("Item not found error while generating chart: " + e.getMessage());
} catch (IllegalArgumentException e) {
logger.debug("Illegal argument in chart", e);
throw new ServletException("Illegal argument in chart: " + e.getMessage());
}
}
private int parseInt(@Nullable String s, int defaultValue) {
if (s == null) {
return defaultValue;
}
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
logger.debug("'{}' is not an integer, using default: {}", s, defaultValue);
return defaultValue;
}
}
@ -175,7 +190,7 @@ public class RRD4jChartServlet implements Servlet, ChartProvider {
label = label.substring(0, label.indexOf('['));
}
try {
RrdDb db = new RrdDb(rrdName);
RrdDb db = RrdDb.of(rrdName);
consolFun = db.getRrdDef().getArcDefs()[0].getConsolFun();
db.close();
} catch (IOException e) {
@ -196,16 +211,16 @@ public class RRD4jChartServlet implements Servlet, ChartProvider {
}
@Override
public void init(ServletConfig config) throws ServletException {
public void init(@Nullable ServletConfig config) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
public @Nullable ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
public @Nullable String getServletInfo() {
return null;
}
@ -222,20 +237,17 @@ public class RRD4jChartServlet implements Servlet, ChartProvider {
}
@Override
public BufferedImage createChart(String service, String theme, Date startTime, Date endTime, int height, int width,
String items, String groups, Integer dpi, Boolean legend) throws ItemNotFoundException {
RrdGraphDef graphDef = new RrdGraphDef();
long period = (startTime.getTime() - endTime.getTime()) / 1000;
public BufferedImage createChart(@Nullable String service, @Nullable String theme, ZonedDateTime startTime,
ZonedDateTime endTime, int height, int width, @Nullable String items, @Nullable String groups,
@Nullable Integer dpi, @Nullable Boolean legend) throws ItemNotFoundException {
RrdGraphDef graphDef = new RrdGraphDef(startTime.toEpochSecond(), endTime.toEpochSecond());
graphDef.setWidth(width);
graphDef.setHeight(height);
graphDef.setAntiAliasing(true);
graphDef.setImageFormat("PNG");
graphDef.setStartTime(period);
graphDef.setTextAntiAliasing(true);
graphDef.setLargeFont(new Font("SansSerif", Font.PLAIN, 15));
graphDef.setSmallFont(new Font("SansSerif", Font.PLAIN, 11));
graphDef.setFont(FontTag.TITLE, new Font("SansSerif", Font.PLAIN, 15));
graphDef.setFont(FontTag.DEFAULT, new Font("SansSerif", Font.PLAIN, 11));
int seriesCounter = 0;
@ -265,19 +277,15 @@ public class RRD4jChartServlet implements Servlet, ChartProvider {
}
// Write the chart as a PNG image
RrdGraph graph;
try {
graph = new RrdGraph(graphDef);
RrdGraph graph = new RrdGraph(graphDef);
BufferedImage bi = new BufferedImage(graph.getRrdGraphInfo().getWidth(),
graph.getRrdGraphInfo().getHeight(), BufferedImage.TYPE_INT_RGB);
graph.render(bi.getGraphics());
return bi;
} catch (IOException e) {
logger.error("Error generating graph.", e);
throw new UncheckedIOException("Error generating RrdGraph", e);
}
return null;
}
@Override