Skip to content

Commit

Permalink
[rrd4j] Upgrade rrd4j, improve exception handling
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
wborn committed Oct 6, 2021
1 parent 61324f8 commit c5ea296
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 74 deletions.
2 changes: 1 addition & 1 deletion bundles/org.openhab.persistence.rrd4j/pom.xml
Expand Up @@ -23,7 +23,7 @@
<dependency>
<groupId>org.rrd4j</groupId>
<artifactId>rrd4j</artifactId>
<version>3.3.1</version>
<version>3.8</version>
</dependency>
</dependencies>
</project>
Expand Up @@ -296,7 +296,7 @@ public Iterable<HistoricItem> query(FilterCriteria filter) {
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;
Expand All @@ -319,7 +319,7 @@ public Set<PersistenceItemInfo> getItemInfo() {
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()) {
Expand All @@ -328,7 +328,7 @@ public Set<PersistenceItemInfo> getItemInfo() {
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.",
Expand All @@ -349,7 +349,7 @@ public Set<PersistenceItemInfo> getItemInfo() {
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;
}
Expand Down Expand Up @@ -543,13 +543,11 @@ protected void modified(final Map<String, Object> config) {
}

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);
}
}
}
Expand Down
Expand Up @@ -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;
Expand All @@ -30,6 +32,9 @@
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;
Expand All @@ -46,6 +51,7 @@
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;
Expand All @@ -66,11 +72,15 @@
* @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";

Expand All @@ -81,28 +91,29 @@ 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<>();

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 Duration DEFAULT_PERIOD = Duration.ofDays(1);

@Reference
protected HttpService httpService;
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))//
);

@Reference
protected ItemUIRegistry itemUIRegistry;
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;
}

@Activate
protected void activate() {
Expand All @@ -125,35 +136,39 @@ protected void deactivate() {
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");
}
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
Date timeEnd = new Date();
Date timeBegin = new Date(timeEnd.getTime() + period);
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;
}
}

Expand All @@ -175,7 +190,7 @@ protected void addLine(RrdGraphDef graphDef, Item item, int counter) {
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) {
Expand All @@ -196,16 +211,16 @@ protected void addLine(RrdGraphDef graphDef, Item item, int counter) {
}

@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;
}

Expand All @@ -222,20 +237,17 @@ public String getName() {
}

@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;

Expand Down Expand Up @@ -265,19 +277,15 @@ public BufferedImage createChart(String service, String theme, Date startTime, D
}

// 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
Expand Down

0 comments on commit c5ea296

Please sign in to comment.