001package org.javasimon.console.action;
002
003import org.javasimon.console.Action;
004import org.javasimon.console.ActionContext;
005import org.javasimon.console.ActionException;
006
007import java.io.IOException;
008import java.io.InputStream;
009import java.io.OutputStream;
010import java.util.HashMap;
011import java.util.Map;
012
013import javax.servlet.ServletException;
014import javax.servlet.http.HttpServletResponse;
015
016/**
017 * Action used to send resources (images, JS, CSS) to the browser.
018 *
019 * @author gquintana
020 */
021public class ResourceAction extends Action {
022
023        public static final String PREFIX = "/resource";
024
025        private static final Map<String, String> CONTENT_TYPES = new HashMap<>();
026
027        static {
028                CONTENT_TYPES.put("gif", "image/gif");
029                CONTENT_TYPES.put("png", "image/png");
030                CONTENT_TYPES.put("jpg", "image/jpeg");
031                CONTENT_TYPES.put("html", "text/html");
032                CONTENT_TYPES.put("css", "text/css");
033                CONTENT_TYPES.put("js", "text/javascript");
034        }
035
036        /**
037         * Relative path of the resource to return.
038         */
039        private final String resourcePath;
040
041        /**
042         * Constructor.
043         *
044         * @param context Action context
045         * @param resourcePath Local path of the resource
046         */
047        public ResourceAction(ActionContext context, String resourcePath) {
048                super(context);
049                if (resourcePath.startsWith("/")) {
050                        this.resourcePath = resourcePath;
051                } else {
052                        this.resourcePath = "/" + resourcePath;
053                }
054        }
055
056        @Override
057        public void execute() throws ServletException, IOException, ActionException {
058                InputStream resourceIStream = null;
059                try {
060                        resourceIStream = getClass().getResourceAsStream("/org/javasimon/console/resource" + resourcePath);
061                        if (resourceIStream == null) {
062                                getContext().getResponse().setStatus(HttpServletResponse.SC_NOT_FOUND);
063                                throw new ActionException("Resource " + resourcePath + " not found");
064                        }
065                        String extension = resourcePath.substring(resourcePath.lastIndexOf('.') + 1).toLowerCase();
066                        String contentType = CONTENT_TYPES.get(extension);
067                        if (contentType != null) {
068                                getContext().setContentType(contentType);
069                                if (contentType.startsWith("text")) {
070                                        getContext().getResponse().setCharacterEncoding("UTF-8");
071                                }
072                        }
073                        getContext().getResponse().setHeader("Cache-Control", "public; max-age=300");
074                        copyStream(resourceIStream);
075                } finally {
076                        if (resourceIStream != null) {
077                                resourceIStream.close();
078                        }
079                }
080        }
081
082        /**
083         * Copy resource input stream to HTTP response output stream using
084         * a 64kib buffer
085         */
086        private void copyStream(InputStream inputStream) throws IOException {
087                OutputStream outputStream = null;
088                try {
089                        outputStream = getContext().getOutputStream();
090                        byte[] buffer = new byte[65535];
091                        int bufferLen;
092                        int totalLen = 0;
093                        while ((bufferLen = inputStream.read(buffer)) > 0) {
094                                outputStream.write(buffer, 0, bufferLen);
095                                totalLen += bufferLen;
096                        }
097                        getContext().getResponse().setContentLength(totalLen);
098                } finally {
099                        if (outputStream != null) {
100                                outputStream.flush();
101                        }
102                }
103        }
104}