PlaceMapServlet.java 13.4 KB
/*
 * Copyright Codecrew Ry
 * 
 * All rights reserved.
 * 
 * This license applies to any software containing a notice placed by the 
 * copyright holder. Such software is herein referred to as the Software. 
 * This license covers modification, distribution and use of the Software. 
 * 
 * Any distribution and use in source and binary forms, with or without 
 * modification is not permitted without explicit written permission from the 
 * copyright owner. 
 * 
 * A non-exclusive royalty-free right is granted to the copyright owner of the 
 * Software to use, modify and distribute all modifications to the Software in 
 * future versions of the Software. 
 * 
 */
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fi.codecrew.moya.servlet;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.EJBAccessException;
import javax.ejb.EJBException;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import fi.codecrew.moya.enums.apps.UserPermission;
import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fi.codecrew.moya.beans.BarcodeBeanLocal;
import fi.codecrew.moya.beans.LoggingBeanLocal;
import fi.codecrew.moya.beans.PermissionBeanLocal;
import fi.codecrew.moya.beans.PlaceBeanLocal;
import fi.codecrew.moya.beans.UserBeanLocal;
import fi.codecrew.moya.enums.apps.MapPermission;
import fi.codecrew.moya.model.EventMap;
import fi.codecrew.moya.model.EventUser;
import fi.codecrew.moya.model.Place;
import fi.codecrew.moya.utilities.moyamessage.MoyaEventType;

/**
 * @author tuukka
 */
@WebServlet("/PlaceMap")
public class PlaceMapServlet extends HttpServlet {

    private static final Logger logger = LoggerFactory.getLogger(PlaceMapServlet.class);

    private static final long serialVersionUID = 8769688627918936258L;
    @EJB
    private transient PlaceBeanLocal placeBean;

    @EJB
    private transient PermissionBeanLocal permbean;

    @EJB
    private transient LoggingBeanLocal loggerbean;

    @EJB
    private UserBeanLocal userBean;

    @EJB
    private BarcodeBeanLocal barcodeBean;

    private static final String PARAMETER_EVENT_MAP_ID = "mapid";
    private static final String PARAMETER_USER_ID = "userid";
    private static final String PARAMETER_TOKEN = "token";
    private static final String PARAMETER_DATE = "date";
    private static final String PARAMETER_TIMESTAMP = "timestamp";

    /**
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code>
     * methods.
     *
     * @param request  servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException      if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        logger.debug("Begin processing request");
        // response.setContentType("text/html;charset=UTF-8");

        // PrintWriter out = response.getWriter();
        ServletOutputStream ostream = null;
        try {

            Integer mapId = getIntegerParameter(request, PARAMETER_EVENT_MAP_ID);

            Long timelapseTs = getLongParameter(request, PARAMETER_TIMESTAMP);
            String timelapseDate = request.getParameter(PARAMETER_DATE);
            Date date = null;
            if (timelapseTs != null) {
                date = new Date(timelapseTs);
            } else if (timelapseDate != null) {
                date = ISODateTimeFormat.dateTime().parseDateTime(timelapseDate).toDate();
            }

            EventMap map = placeBean.findMap(mapId);
            logger.debug("Mapid: {}", mapId);
            ostream = response.getOutputStream();

            if (map == null || map.getMapData() == null) {
                logger.warn("Error handling map {}!", map);
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                ostream.print("Map error!");
                return;
            }
            String[] splittype = map.getMimeType().split("/");
            String type = "png";

            if (splittype.length > 0) {
                type = splittype[splittype.length - 1];
            }

            response.setContentType(map.getMimeType());

            if (date != null) {
                printTimelapseToStream(ostream, type, map, date);
            } else {
                Integer userId = getIntegerParameter(request, PARAMETER_USER_ID);
                String userToken = request.getParameter(PARAMETER_TOKEN);

                EventUser user = null;
                if (userToken != null) {
                    user = barcodeBean.getUserFromLongTextCode(userToken);
                }

                if (!permbean.hasPermission(MapPermission.VIEW) && user == null) {
                    loggerbean.sendMessage(MoyaEventType.USER_INSUFFICIENT_PERMISSIONS, permbean.getCurrentUser(),
                            "User tried to print the placemap to stream without sufficient permissions");
                    throw new EJBAccessException("Not enough permissions to print placemap");
                }
                // Find for user only with sufficient permissions
                if (user != null && userId != 0 && permbean.hasPermission(UserPermission.VIEW_ALL)) {
                    user = userBean.findByUserId(userId, false);
                }

                printPlaceMapToStream(ostream, type, map, user);

            }

            ostream.flush();

        } catch (EJBException e) {

            logger.debug("Permission denied. Returning SC_FORBIDDEN!");
            response.setContentType("text/html;charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            ostream = response.getOutputStream();
            ostream.print("Error 403 \nPermission denied! Please login before accessing resource");
            // e.printStackTrace();

        } finally {
            if (ostream != null) {
                ostream.close();
            }
        }
    }

    private void printTimelapseToStream(OutputStream ostr, String filetype, EventMap map, Date date) throws IOException {
        BufferedImage image = drawMap(map, new TimelapsePicker(date));
        ImageIO.write(image, filetype, ostr);
    }

    private void printPlaceMapToStream(OutputStream outputStream, String filetype, EventMap map, EventUser user) throws IOException {

        List<Place> userplaces = null;
        if (user != null) {
            userplaces = placeBean.findPlacePrintlistForUser(user);
        }
        // Yes this is is in correct order.
        if (user == null) {
            user = permbean.getCurrentUser();
        }

        BufferedImage image = drawMap(map, new LegacyColorPicker(user, userplaces));
        ImageIO.write(image, filetype, outputStream);
    }

    private BufferedImage drawMap(EventMap map, PlaceColorPicker colorPicker) throws IOException {
        BufferedImage image = ImageIO.read(new ByteArrayInputStream(map.getMapData()));

        Graphics2D g2d = image.createGraphics();
        for (Place place : map.getPlaces()) {
            drawPlace(place, g2d, colorPicker);
        }
        return image;
    }


    /***
     * Convert request parameter into integer
     *
     * @param request
     * @param parameter
     * @return
     */
    private static Integer getIntegerParameter(HttpServletRequest request, String parameter) {
        try {
            String valueString = request.getParameter(parameter);
            Integer value = Integer.parseInt(valueString);
            return value;
        } catch (NumberFormatException nfe) {
        }
        return null;
    }
    private static Long getLongParameter(HttpServletRequest request, String parameter) {
        try {
            String valueString = request.getParameter(parameter);
            Long value = Long.parseLong(valueString);
            return value;
        } catch (NumberFormatException nfe) {
        }
        return null;
    }

    // <editor-fold defaultstate="collapsed"
    // desc="HttpServlet methods. Click on the + sign on the left to edit the
    // code.">

    /**
     * Handles the HTTP <code>GET</code> method.
     *
     * @param request  servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException      if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Handles the HTTP <code>POST</code> method.
     *
     * @param request  servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException      if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>

    private static final Color RESERVED_COLOR = Color.RED;
    private static final Color SELECTED_COLOR = Color.BLUE;
    private static final Color OWNED_COLOR = Color.GREEN;
    private static final Color BORDER_COLOR = Color.BLACK;
    private static final Color LOCKED_COLOR = Color.DARK_GRAY;

    private static interface PlaceColorPicker {
        Color getColor(Place p);
    }

    public static class TimelapsePicker implements PlaceColorPicker {

        private final Date date;

        public TimelapsePicker(Date date) {
            this.date = date;
        }

        @Override
        public Color getColor(Place p) {

            if (p.getReserverSlot() != null && date.after(p.getReserverSlot().getUsed())) {
                return RESERVED_COLOR;
            }
            return null;
        }
    }

    public static class LegacyColorPicker implements PlaceColorPicker {
        private final EventUser user;
        // private final boolean onlyFrame;
        // private final boolean hilight;
        private final List<Place> userplaces;

        // public LegacyColorPicker(EventUser u, boolean onlyFrame, boolean
        // hilight) {
        // this.user = u;
        // this.onlyFrame = onlyFrame;
        // this.hilight = hilight;
        // }

        public LegacyColorPicker(EventUser user2, List<Place> userplaces) {
            this.user = user2;
            this.userplaces = userplaces;

        }

        public Color getColor(Place p) {

            Color color = null;
            // Check if we wan
            if (userplaces == null) {
                if (!p.isBuyable()) {
                    logger.debug("Setting color as locked place.");
                    color = LOCKED_COLOR;
                }

                if (p.isReservedFor(user)) {
                    // logger.debug("Setting place selected {}", p);
                    color = SELECTED_COLOR;
                } else if (user.equals(p.getCurrentUser())
                        || (p.getGroup() != null && user.equals(p.getGroup().getCreator()))
                        || (p.getPlaceReserver() != null && user.equals(p.getPlaceReserver().getUser()))) {
                    color = OWNED_COLOR;
                    // logger.debug("Setting place owned {}", p);

                } else if (p.isTaken()) {
                    color = RESERVED_COLOR;
                    // logger.debug("Setting place Reserved {}", p);

                } else if (p.getProduct() != null && p.getProduct() != null && p.getProduct().getColor() != null
                        && !p.getProduct().getColor().isEmpty()) {
                    try {
                        color = Color.decode(p.getProduct().getColor());
                    } catch (NumberFormatException x) {
                        logger.error("Cannot convert string {} to color.", p.getProduct().getColor());
                    }
                } else {
                    // too much debugging -TKjne
                    // logger.debug("Nothing special for this place. Color
                    // should be default.");

                }
            } else if (userplaces.contains(p)) {
                color = OWNED_COLOR;
            }
            return color;
        }
    }

    ;

    private static void drawPlace(Place p, Graphics2D g, PlaceColorPicker colorPicker) {
        if (p.isDisabled()) {
            return;
        }

        Color color = colorPicker.getColor(p);

        if (color != null) {
            // logger.debug("Setting fill color: {} for p", color, p);
            g.setColor(color);
            g.fill(new Rectangle(p.getMapX() + 1, p.getMapY() + 1, p.getWidth() - 1, p.getHeight() - 1));
        }

        g.setColor(BORDER_COLOR);
        g.draw(new Rectangle(p.getMapX(), p.getMapY(), p.getWidth(), p.getHeight()));
    }
}