All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.zhengframework.webjars.WebjarsServlet Maven / Gradle / Ivy

package com.github.zhengframework.webjars;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.StringJoiner;
import javax.inject.Inject;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.webjars.NotFoundException;
import org.webjars.WebJarAssetLocator;

@Slf4j
public class WebjarsServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;

  private static final long DEFAULT_EXPIRE_TIME_MS = 86400000L; // 1 day
  private static final long DEFAULT_EXPIRE_TIME_S = 86400L; // 1 day

  private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
  private static final int EOF = -1;
  private final WebjarsConfig webjarsConfig;
  private boolean disableCache = false;
  private WebJarAssetLocator locator = new WebJarAssetLocator();

  @Inject
  public WebjarsServlet(WebjarsConfig webjarsConfig) {
    this.webjarsConfig = webjarsConfig;
  }

  private static boolean isDirectoryRequest(String uri) {
    return uri.endsWith("/");
  }

  /**
   * Copy bytes from an InputStream to an
   * OutputStream.
   * 

* This method buffers the input internally, so there is no need to use a * BufferedInputStream. *

* Large streams (over 2GB) will return a bytes copied value of * -1 after the copy has completed since the correct * number of bytes cannot be returned as an int. For large streams use the * copyLarge(InputStream, OutputStream) method. * * @param input the InputStream to read from * @param output the OutputStream to write to * @return the number of bytes copied, or -1 if > Integer.MAX_VALUE * @throws NullPointerException if the input or output is null * @throws IOException if an I/O error occurs * @since 1.1 */ private static int copy(InputStream input, OutputStream output) throws IOException { long count = 0; int n = 0; byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; while (EOF != (n = input.read(buffer))) { output.write(buffer, 0, n); count += n; } if (count > Integer.MAX_VALUE) { return -1; } return (int) count; } @Override public void init() throws ServletException { ServletConfig config = getServletConfig(); if (config == null) { throw new NullPointerException( "Expected servlet container to provide a non-null ServletConfig."); } try { String disableCache = config.getInitParameter("disableCache"); if (disableCache != null) { this.disableCache = Boolean.parseBoolean(disableCache); log.info("WebjarsServlet cache enabled: {}", !this.disableCache); } } catch (Exception e) { log.warn("The WebjarsServlet configuration parameter \"disableCache\" is invalid"); } log.info("WebjarsServlet initialization completed"); } private String getFullPath(String uri) throws NotFoundException { log.info("uri={}", uri); if (uri.startsWith("/")) { uri = uri.substring(1); } String[] strings = uri.split("/"); log.info("str={}", Arrays.toString(strings)); StringJoiner stringJoiner = new StringJoiner("/"); for (int i = 1; i < strings.length; i++) { stringJoiner.add(strings[i]); } try { return locator.getFullPath(strings[0], stringJoiner.toString()); } catch (NullPointerException e) { log.error("get full path error, uri={}", uri, e); throw new NotFoundException(uri); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { log.info("requestURI: {}", request.getRequestURI()); log.info("getContextPath: {}", request.getContextPath()); String uri = request.getRequestURI() .replaceFirst(request.getContextPath(), "") .replaceFirst(webjarsConfig.getBasePath(), ""); if (isDirectoryRequest(uri)) { response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } String webjarsResourceURI; try { webjarsResourceURI = getFullPath(uri); } catch (NotFoundException e) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } log.info("Webjars resource requested: {}", webjarsResourceURI); String eTagName; try { eTagName = this.getETagName(webjarsResourceURI); } catch (IllegalArgumentException e) { log.info("etag"); response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } if (!disableCache) { if (checkETagMatch(request, eTagName) || checkLastModify(request)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return; } } InputStream inputStream = this.getClass().getResourceAsStream("/" + webjarsResourceURI); if (inputStream != null) { try { if (!disableCache) { prepareCacheHeaders(response, eTagName); } String filename = getFileName(webjarsResourceURI); String mimeType = this.getServletContext().getMimeType(filename); response.setContentType(mimeType != null ? mimeType : "application/octet-stream"); copy(inputStream, response.getOutputStream()); } finally { inputStream.close(); } } else { log.error("read"); // return HTTP error response.sendError(HttpServletResponse.SC_NOT_FOUND); } } private String getFileName(String webjarsResourceURI) { String[] tokens = webjarsResourceURI.split("/"); return tokens[tokens.length - 1]; } private String getETagName(String webjarsResourceURI) { return webjarsResourceURI; } /* Important!!*/ /* The code bellow has been copied from apache Commons IO. More specifically from its IOUtils class. */ /* The reason is becasue I don't want to include any more dependencies */ private boolean checkETagMatch(HttpServletRequest request, String eTagName) { String token = request.getHeader("If-None-Match"); return (token != null && token.equals(eTagName)); } private boolean checkLastModify(HttpServletRequest request) { long last = request.getDateHeader("If-Modified-Since"); return (last != -1L && (last - System.currentTimeMillis() > 0L)); } private void prepareCacheHeaders(HttpServletResponse response, String eTag) { response.setHeader("ETag", eTag); response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME_MS); response.addDateHeader("Last-Modified", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME_MS); response.addHeader("Cache-Control", "private, max-age=" + DEFAULT_EXPIRE_TIME_S); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy