Java tutorial
/** * Copyright (C) 2013 * by 52 North Initiative for Geospatial Open Source Software GmbH * * Contact: Andreas Wytzisk * 52 North Initiative for Geospatial Open Source Software GmbH * Martin-Luther-King-Weg 24 * 48155 Muenster, Germany * info@52north.org * * This program is free software; you can redistribute and/or modify it under * the terms of the GNU General Public License version 2 as published by the * Free Software Foundation. * * This program is distributed WITHOUT ANY WARRANTY; even without the implied * WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program (see gnu-gpl v2.txt). If not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA or * visit the Free Software Foundation web page, http://www.fsf.org. */ package org.n52.io; import static org.n52.io.IoParameters.GRID; import static org.n52.io.IoParameters.HEIGHT; import static org.n52.io.IoParameters.LOCALE; import static org.n52.io.IoParameters.PHENOMENON; import static org.n52.io.IoParameters.WIDTH; import static org.n52.io.img.RenderingContext.createContextForSingleTimeseries; import static org.n52.io.v1.data.UndesignedParameterSet.createForSingleTimeseries; import static org.n52.web.v1.ctrl.Stopwatch.startStopwatch; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import javax.imageio.ImageIO; import javax.servlet.ServletConfig; import javax.servlet.ServletOutputStream; import org.joda.time.DateTime; import org.joda.time.Interval; import org.n52.io.TaskConfigPrerendering.ConfiguredStyle; import org.n52.io.format.TvpDataCollection; import org.n52.io.img.ChartDimension; import org.n52.io.img.RenderingContext; import org.n52.io.v1.data.PhenomenonOutput; import org.n52.io.v1.data.StyleProperties; import org.n52.io.v1.data.TimeseriesMetadataOutput; import org.n52.io.v1.data.UndesignedParameterSet; import org.n52.web.ResourceNotFoundException; import org.n52.web.v1.ctrl.Stopwatch; import org.n52.web.v1.srv.ParameterService; import org.n52.web.v1.srv.TimeseriesDataService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.context.ServletConfigAware; import com.fasterxml.jackson.databind.ObjectMapper; public class PreRenderingTask implements ServletConfigAware { private final static Logger LOGGER = LoggerFactory.getLogger(PreRenderingTask.class); private static final String TASK_CONFIG_FILE = "/task-config-prerendering.json"; private ParameterService<TimeseriesMetadataOutput> timeseriesMetadataService; private TimeseriesDataService timeseriesDataService; private TaskConfigPrerendering taskConfigPrerendering; private RenderTask taskToRun; private String webappFolder; private String outputPath; private int periodInMinutes; private boolean enabled; private int width = 800; private int height = 500; private String language = "en"; private boolean showGrid = true; // factory method public static PreRenderingTask createTask() { return new PreRenderingTask(); } // destroy method public void shutdownTask() { this.enabled = false; this.taskToRun.cancel(); LOGGER.info("Render task successfully shutted down."); } PreRenderingTask() { this.taskToRun = new RenderTask(); this.taskConfigPrerendering = readTaskConfig(); } private TaskConfigPrerendering readTaskConfig() { InputStream taskConfig = getClass().getResourceAsStream(TASK_CONFIG_FILE); try { ObjectMapper om = new ObjectMapper(); return om.readValue(taskConfig, TaskConfigPrerendering.class); } catch (IOException e) { LOGGER.error("Could not load {}. Using empty config.", TASK_CONFIG_FILE, e); return new TaskConfigPrerendering(); } finally { if (taskConfig != null) { try { taskConfig.close(); } catch (IOException e) { LOGGER.debug("Stream already closed."); } } } } public void startTask() { if (taskToRun != null) { this.enabled = true; Timer timer = new Timer("Prerender charts timer task"); timer.schedule(taskToRun, 10000, getPeriodInMilliseconds()); } } private int getPeriodInMilliseconds() { return 1000 * 60 * periodInMinutes; } @Override public void setServletConfig(ServletConfig servletConfig) { webappFolder = servletConfig.getServletContext().getRealPath("/"); } public String getOutputPath() { return outputPath; } public void setOutputPath(String outputPath) { this.outputPath = outputPath; } public ParameterService<TimeseriesMetadataOutput> getTimeseriesMetadataService() { return timeseriesMetadataService; } public void setTimeseriesMetadataService(ParameterService<TimeseriesMetadataOutput> timeseriesMetadataService) { this.timeseriesMetadataService = timeseriesMetadataService; } public TimeseriesDataService getTimeseriesDataService() { return timeseriesDataService; } public void setTimeseriesDataService(TimeseriesDataService timeseriesDataService) { this.timeseriesDataService = timeseriesDataService; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } public boolean isShowGrid() { return showGrid; } public void setShowGrid(boolean showGrid) { this.showGrid = showGrid; } public int getPeriodInMinutes() { return periodInMinutes; } public void setPeriodInMinutes(int period) { this.periodInMinutes = period; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; if (enabled) { startTask(); } } public boolean hasPrerenderedImage(String timeseriesId, String interval) { File name = createFileName(timeseriesId, interval); return name.exists(); } public void writePrerenderedGraphToOutputStream(String timeseriesId, String interval, ServletOutputStream outputStream) { try { BufferedImage image = loadImage(timeseriesId, interval); if (image == null) { ResourceNotFoundException ex = new ResourceNotFoundException("Could not find image on server."); ex.addHint("Perhaps the image is being rendered at the moment. Try again later."); throw ex; } ImageIO.write(image, "png", outputStream); } catch (IOException e) { LOGGER.error("Error while loading pre rendered image", e); } } private BufferedImage loadImage(String timeseriesId, String interval) throws IOException { return ImageIO.read(new FileInputStream(createFileName(timeseriesId, interval))); } public Interval createTimespanFromInterval(String timeseriesId, String interval) { DateTime now = new DateTime(); if (interval.equals("lastDay")) { return new Interval(now.minusDays(1), now); } else if (interval.equals("lastWeek")) { return new Interval(now.minusWeeks(1), now); } else if (interval.equals("lastMonth")) { return new Interval(now.minusMonths(1), now); } else { throw new ResourceNotFoundException( "Unknown interval definition '" + interval + "' for timeseriesId " + timeseriesId); } } private FileOutputStream createFile(String timeseriesId, String interval) throws IOException { File file = createFileName(timeseriesId, interval); file.createNewFile(); return new FileOutputStream(file); } private File createFileName(String timeseriesId, String interval) { String outputDirectory = getOutputFolder(); String filename = timeseriesId + "_" + interval + ".png"; return new File(outputDirectory + filename); } private String getOutputFolder() { String outputDirectory = webappFolder + File.separator + outputPath + File.separator; File dir = new File(outputDirectory); if (!dir.exists()) { dir.mkdirs(); } return outputDirectory; } private IoParameters createConfig() { Map<String, String> configuration = new HashMap<String, String>(); configuration.put(WIDTH, Integer.toString(width)); configuration.put(HEIGHT, Integer.toString(height)); configuration.put(GRID, Boolean.toString(showGrid)); configuration.put(LOCALE, language); return IoParameters.createFromQuery(configuration); } private TvpDataCollection getTimeseriesData(UndesignedParameterSet parameters) { Stopwatch stopwatch = startStopwatch(); TvpDataCollection timeseriesData = timeseriesDataService.getTimeseriesData(parameters); LOGGER.debug("Processing request took {} seconds.", stopwatch.stopInSeconds()); return timeseriesData; } private final class RenderTask extends TimerTask { @Override public void run() { LOGGER.info("Start prerendering task"); try { Map<String, ConfiguredStyle> phenomenonStyles = taskConfigPrerendering.getPhenomenonStyles(); Map<String, ConfiguredStyle> timeseriesStyles = taskConfigPrerendering.getTimeseriesStyles(); for (String phenomenonId : phenomenonStyles.keySet()) { Map<String, String> parameters = new HashMap<String, String>(); parameters.put(PHENOMENON, phenomenonId); IoParameters query = IoParameters.createFromQuery(parameters); TimeseriesMetadataOutput[] metadatas = timeseriesMetadataService.getCondensedParameters(query); for (TimeseriesMetadataOutput metadata : metadatas) { String timeseriesId = metadata.getId(); ConfiguredStyle style = timeseriesStyles.containsKey(timeseriesId) ? timeseriesStyles.get(timeseriesId) : phenomenonStyles.get(phenomenonId); renderConfiguredIntervals(timeseriesId, style); } } for (String timeseriesId : timeseriesStyles.keySet()) { TimeseriesMetadataOutput metadata = timeseriesMetadataService.getParameter(timeseriesId); PhenomenonOutput phenomenon = metadata.getParameters().getPhenomenon(); if (!phenomenonStyles.containsKey(phenomenon.getId())) { // overridden phenomena styles have been rendered already ConfiguredStyle style = timeseriesStyles.get(timeseriesId); renderConfiguredIntervals(timeseriesId, style); } } } catch (IOException e) { LOGGER.error("Error while reading prerendering configuration file", e); } } private void renderConfiguredIntervals(String timeseriesId, ConfiguredStyle style) throws IOException { for (String interval : style.getInterval()) { renderWithStyle(timeseriesId, style.getStyle(), interval); } } private void renderWithStyle(String timeseriesId, StyleProperties style, String interval) throws IOException { IoParameters config = createConfig(); Interval timespan = createTimespanFromInterval(timeseriesId, interval); TimeseriesMetadataOutput metadata = timeseriesMetadataService.getParameter(timeseriesId, config); RenderingContext context = createContextForSingleTimeseries(metadata, style, timespan); context.setDimensions(new ChartDimension(width, height)); UndesignedParameterSet parameters = createForSingleTimeseries(timeseriesId, timespan); IoHandler renderer = IoFactory.createWith(config).createIOHandler(context); FileOutputStream fos = createFile(timeseriesId, interval); renderChartFile(renderer, parameters, fos); } private void renderChartFile(IoHandler renderer, UndesignedParameterSet parameters, FileOutputStream fos) { try { renderer.generateOutput(getTimeseriesData(parameters)); renderer.encodeAndWriteTo(fos); } catch (IoParseException e) { LOGGER.error("Image creation occures error.", e); } finally { try { fos.flush(); fos.close(); } catch (IOException e) { LOGGER.error("File stream already flushed/closed.", e); } } } } }