/** * Copyright (c) 2014-2017 by the respective copyright holders. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.smarthome.ui.icon.internal; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Date; import java.util.Hashtable; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.eclipse.smarthome.ui.icon.IconProvider; import org.eclipse.smarthome.ui.icon.IconSet.Format; import org.osgi.service.http.HttpContext; import org.osgi.service.http.HttpService; import org.osgi.service.http.NamespaceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Registers a servlet that serves icons through {@link IconProvider}s. * * @author Kai Kreuzer - Initial contribution */ public class IconServlet extends HttpServlet { private static final long serialVersionUID = 2880642275858634578L; private final Logger logger = LoggerFactory.getLogger(IconServlet.class); private static final String SERVLET_NAME = "/icon"; private static final String PARAM_ICONSET = "iconset"; private static final String PARAM_FORMAT = "format"; private static final String PARAM_STATE = "state"; private long startupTime; protected HttpService httpService; protected String defaultIconSetId = "classic"; private List<IconProvider> iconProvider = new ArrayList<>(); public void setHttpService(HttpService httpService) { this.httpService = httpService; } public void unsetHttpService(HttpService httpService) { this.httpService = null; } public void addIconProvider(IconProvider iconProvider) { this.iconProvider.add(iconProvider); } public void removeIconProvider(IconProvider iconProvider) { this.iconProvider.remove(iconProvider); } protected void activate(Map<String, Object> config) { try { logger.debug("Starting up icon servlet at " + SERVLET_NAME); Hashtable<String, String> props = new Hashtable<String, String>(); httpService.registerServlet(SERVLET_NAME, this, props, createHttpContext()); } catch (NamespaceException e) { logger.error("Error during servlet startup", e); } catch (ServletException e) { logger.error("Error during servlet startup", e); } startupTime = System.currentTimeMillis(); modified(config); } protected void modified(Map<String, Object> config) { Object iconSetId = config.get("default"); if (iconSetId instanceof String) { defaultIconSetId = (String) iconSetId; } } /** * Creates a {@link HttpContext} * * @return a {@link HttpContext} */ protected HttpContext createHttpContext() { HttpContext defaultHttpContext = httpService.createDefaultHttpContext(); return defaultHttpContext; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (req.getDateHeader("If-Modified-Since") > startupTime) { resp.setStatus(304); return; } String category = getCategory(req); Format format = getFormat(req); String state = getState(req); String iconSetId = getIconSetId(req); IconProvider topProvider = null; int maxPrio = Integer.MIN_VALUE; for (IconProvider provider : iconProvider) { Integer prio = provider.hasIcon(category, iconSetId, format); if (prio != null && prio > maxPrio) { maxPrio = prio; topProvider = provider; } } if (topProvider != null) { if (format.equals(Format.SVG)) { resp.setContentType("image/svg+xml"); } else { resp.setContentType("image/png"); } resp.setDateHeader("Last-Modified", new Date().getTime()); ServletOutputStream os = resp.getOutputStream(); try (InputStream is = topProvider.getIcon(category, iconSetId, state, format)) { IOUtils.copy(is, os); resp.flushBuffer(); } catch (IOException e) { logger.error("Failed sending the icon byte stream as a response: {}", e.getMessage()); resp.sendError(500, e.getMessage()); } } else { resp.sendError(404); } } private String getCategory(HttpServletRequest req) { String category = StringUtils.substringAfterLast(req.getRequestURI(), "/"); category = StringUtils.substringBeforeLast(category, "."); return StringUtils.substringBeforeLast(category, "-"); } private Format getFormat(HttpServletRequest req) { String format = req.getParameter(PARAM_FORMAT); if (format == null) { String filename = StringUtils.substringAfterLast(req.getRequestURI(), "/"); format = StringUtils.substringAfterLast(filename, "."); } try { Format f = Format.valueOf(format.toUpperCase()); return f; } catch (IllegalArgumentException e) { logger.debug("unknown format '{}' in HTTP request - falling back to PNG", format); return Format.PNG; } } private String getIconSetId(HttpServletRequest req) { String iconSetId = req.getParameter(PARAM_ICONSET); if (iconSetId == null || iconSetId.isEmpty()) { return defaultIconSetId; } else { return iconSetId; } } private String getState(HttpServletRequest req) { String state = req.getParameter(PARAM_STATE); if (state != null) { return state; } else { String filename = StringUtils.substringAfterLast(req.getRequestURI(), "/"); state = StringUtils.substringAfterLast(filename, "-"); state = StringUtils.substringBeforeLast(state, "."); if (StringUtils.isNotEmpty(state)) { return state; } else { return null; } } } }