Example usage for javax.servlet.http HttpServletResponse SC_NOT_MODIFIED

List of usage examples for javax.servlet.http HttpServletResponse SC_NOT_MODIFIED

Introduction

In this page you can find the example usage for javax.servlet.http HttpServletResponse SC_NOT_MODIFIED.

Prototype

int SC_NOT_MODIFIED

To view the source code for javax.servlet.http HttpServletResponse SC_NOT_MODIFIED.

Click Source Link

Document

Status code (304) indicating that a conditional GET operation found that the resource was available and not modified.

Usage

From source file:fi.hoski.web.auth.LoginServlet.java

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    response.setCharacterEncoding("UTF-8");

    String email = request.getParameter("email");
    String activationKey = request.getParameter("activationKey");
    try {/*ww  w. j ava  2 s. c  o  m*/
        if (email != null && activationKey != null) {
            Map<String, Object> user = userDirectory.useActivationKey(email, activationKey);

            if (user != null) {
                HttpSession session = request.getSession(true);
                session.setAttribute(USER, user);
            }

            // redirect always, if user is not logged in,
            // there will be a login screen
            response.sendRedirect("/member"); //TODO target make configurable
        } else {
            HttpSession session = request.getSession(false);
            String etag = request.getHeader("If-None-Match");
            @SuppressWarnings("unchecked")
            Map<String, Object> user = (session != null) ? (Map<String, Object>) session.getAttribute(USER)
                    : null;
            String userEtag = getEtag(user);

            if (etag != null && etag.equals(userEtag)) {
                response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            } else {
                response.setHeader("ETag", userEtag);
                response.setHeader("Cache-Control", "private");
                response.setHeader("Vary", "Cookie");

                writeUserJSON(user, response);
            }
        }
    } catch (UnavailableException ex) {
        log(ex.getMessage(), ex);
        response.sendError(HttpServletResponse.SC_FORBIDDEN, ex.getMessage());
    } catch (EmailNotUniqueException ex) {
        log(ex.getMessage(), ex);
        response.sendError(HttpServletResponse.SC_FORBIDDEN, ex.getMessage());
    }
}

From source file:com.cloud.servlet.StaticResourceServletTest.java

@Test
public void testWithEtag() throws ServletException, IOException {
    final HashMap<String, String> headers = new HashMap<String, String>();
    headers.put("If-None-Match", StaticResourceServlet.getEtag(new File(rootDirectory, "default.css")));
    final HttpServletResponse response = doGetTest("default.css", headers);
    Mockito.verify(response).setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}

From source file:org.auraframework.http.AuraResourceServlet.java

/**
 * check the top level component/app./*from ww  w .j a  va 2s . c o m*/
 * 
 * This routine checks to see that we have a valid top level component. If our top level component is out of sync,
 * we have to ignore it here, but we _must_ force the client to not cache the response.
 * 
 * If there is a QFE, we substitute the QFE descriptor for the one given us, and continue. Again, we cannot allow
 * caching.
 * 
 * Finally, if there is no descriptor given, we simply ignore the request and give them an empty response. Which is
 * done here by returning null.
 * 
 * Also note that this handles the 'if-modified-since' header, as we want to tell the browser that nothing changed
 * in that case.
 * 
 * @param request the request (for exception handling)
 * @param response the response (for exception handling)
 * @param context the context to get the definition.
 * @return the set of descriptors we are sending back, or null in the case that we handled the response.
 * @throws IOException if there was an IO exception handling a client out of sync exception
 * @throws ServletException if there was a problem handling the out of sync
 */
private Set<DefDescriptor<?>> handleTopLevel(HttpServletRequest request, HttpServletResponse response,
        AuraContext context) throws IOException, ServletException {
    DefDescriptor<? extends BaseComponentDef> appDesc = context.getApplicationDescriptor();
    DefinitionService definitionService = Aura.getDefinitionService();
    MasterDefRegistry mdr = context.getDefRegistry();

    context.setPreloading(true);
    if (appDesc == null) {
        //
        // This means we have nothing to say to the client, so the response is
        // left completely empty.
        //
        return null;
    }
    long ifModifiedSince = request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
    String uid = context.getUid(appDesc);
    try {
        try {
            definitionService.updateLoaded(appDesc);
            if (uid != null && ifModifiedSince != -1) {
                //
                // In this case, we have an unmodified descriptor, so just tell
                // the client that.
                //
                response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
                return null;
            }
        } catch (ClientOutOfSyncException coose) {
            //
            // We can't actually handle an out of sync here, since we are doing a
            // preload. We have to ignore it, and continue as if nothing happened.
            // But in the process, we make sure to set 'no-cache' so that the result
            // is thrown away. This may actually not give the right result in bizarre
            // corner cases... beware cache inconsistencied on revert after a QFE.
            //
            // We actually probably should do something different, like send a minimalist
            // set of stuff to make the client re-try.
            //
            setNoCache(response);
            String oosUid = mdr.getUid(null, appDesc);
            return mdr.getDependencies(oosUid);
        }
    } catch (QuickFixException qfe) {
        DefDescriptor<ComponentDef> qfeDescriptor;

        //
        // A quickfix exception means that we couldn't compile something.
        // In this case, we still want to preload things, but we want to preload
        // quick fix values, note that we force NoCache here.
        //
        setNoCache(response);

        qfeDescriptor = definitionService.getDefDescriptor("markup://auradev:quickFixException",
                ComponentDef.class);
        context.setLoadingApplicationDescriptor(qfeDescriptor);
        String qfeUid;
        try {
            qfeUid = mdr.getUid(null, qfeDescriptor);
        } catch (QuickFixException death) {
            //
            // Ok, we really can't handle this here, so just punt. This means that
            // the quickfix display is broken, and whatever we try will give us grief.
            //
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return null;
        }
        return mdr.getDependencies(qfeUid);
    }
    setLongCache(response);
    if (uid == null) {
        uid = context.getUid(appDesc);
    }
    return mdr.getDependencies(uid);
}

From source file:org.xwiki.resource.servlet.AbstractServletResourceReferenceHandler.java

/**
 * @param resourceReference the reference of the requested resource
 * @return {@code true} if the requested resource is static and is cached by the browser, {@code false} if the
 *         browser should discard the cached version and use the new version from this response
 *//*  w ww .j  av  a 2 s  . co  m*/
private boolean shouldBrowserUseCachedContent(R resourceReference) {
    // If the request contains an "If-Modified-Since" header and the requested resource has not been modified then
    // return a 304 Not Modified to tell the browser to use its cached version.
    Request request = this.container.getRequest();
    if (request instanceof ServletRequest
            && ((ServletRequest) request).getHttpServletRequest().getHeader("If-Modified-Since") != null
            && isResourceCacheable(resourceReference)) {
        // The user probably used F5 to reload the page and the browser checks if there are changes.
        Response response = this.container.getResponse();
        if (response instanceof ServletResponse) {
            // Return the 304 Not Modified.
            ((ServletResponse) response).getHttpServletResponse()
                    .setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return true;
        }
    }
    return false;
}

From source file:eionet.eunis.servlets.DownloadServlet.java

/**
 * Process the actual request.//from   w  w w.  j  a v  a2s.  c  o  m
 *
 * @param request The request to be processed.
 * @param response The response to be created.
 * @param content Whether the request body should be written (GET) or not (HEAD).
 * @throws IOException If something fails at I/O level.
 * @throws ServletException
 */
private void processRequest(HttpServletRequest request, HttpServletResponse response, boolean content)
        throws IOException, ServletException {

    String requestURI = request.getRequestURI();
    String contextPath = request.getContextPath();
    String pathInfo = request.getPathInfo();
    String servletPath = request.getServletPath();

    // Create the abstract file reference to the requested file.
    File file = null;
    String fileRelativePath = StringUtils.substringAfter(request.getRequestURI(), request.getContextPath());
    fileRelativePath = StringUtils.replace(fileRelativePath, "%20", " ");
    if (StringUtils.isNotEmpty(fileRelativePath) && StringUtils.isNotEmpty(appHome)) {
        file = new File(appHome, fileRelativePath);
    }

    // If file was not found, send 404.
    if (file == null || !file.exists() || file.isDirectory()) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    // Prepare some variables. The ETag is an unique identifier of the file.
    String fileName = file.getName();
    long length = file.length();
    long lastModified = file.lastModified();
    String eTag = fileName + "_" + length + "_" + lastModified;

    // Validate request headers for caching ---------------------------------------------------

    // If-None-Match header should contain "*" or ETag. If so, then return 304.
    String ifNoneMatch = request.getHeader("If-None-Match");
    if (ifNoneMatch != null && matches(ifNoneMatch, eTag)) {
        response.setHeader("ETag", eTag); // Required in 304.
        response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
        return;
    }

    // If-Modified-Since header should be greater than LastModified. If so, then return 304.
    // This header is ignored if any If-None-Match header is specified.
    long ifModifiedSince = request.getDateHeader("If-Modified-Since");
    if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
        response.setHeader("ETag", eTag); // Required in 304.
        response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
        return;
    }

    // Validate request headers for resume ----------------------------------------------------

    // If-Match header should contain "*" or ETag. If not, then return 412.
    String ifMatch = request.getHeader("If-Match");
    if (ifMatch != null && !matches(ifMatch, eTag)) {
        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
        return;
    }

    // If-Unmodified-Since header should be greater than LastModified. If not, then return 412.
    long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since");
    if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) {
        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
        return;
    }

    // Validate and process range -------------------------------------------------------------

    // Prepare some variables. The full Range represents the complete file.
    Range full = new Range(0, length - 1, length);
    List<Range> ranges = new ArrayList<Range>();

    // Validate and process Range and If-Range headers.
    String range = request.getHeader("Range");
    if (range != null) {

        // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416.
        if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) {
            response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return;
        }

        // If-Range header should either match ETag or be greater then LastModified. If not,
        // then return full file.
        String ifRange = request.getHeader("If-Range");
        if (ifRange != null && !ifRange.equals(eTag)) {
            try {
                long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid.
                if (ifRangeTime != -1 && ifRangeTime + 1000 < lastModified) {
                    ranges.add(full);
                }
            } catch (IllegalArgumentException ignore) {
                ranges.add(full);
            }
        }

        // If any valid If-Range header, then process each part of byte range.
        if (ranges.isEmpty()) {
            for (String part : range.substring(6).split(",")) {
                // Assuming a file with length of 100, the following examples returns bytes at:
                // 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100).
                long start = sublong(part, 0, part.indexOf("-"));
                long end = sublong(part, part.indexOf("-") + 1, part.length());

                if (start == -1) {
                    start = length - end;
                    end = length - 1;
                } else if (end == -1 || end > length - 1) {
                    end = length - 1;
                }

                // Check if Range is syntactically valid. If not, then return 416.
                if (start > end) {
                    response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
                    response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                    return;
                }

                // Add range.
                ranges.add(new Range(start, end, length));
            }
        }
    }

    // Prepare and initialize response --------------------------------------------------------

    // Get content type by file name and set default GZIP support and content disposition.
    String contentType = getServletContext().getMimeType(fileName);
    boolean acceptsGzip = false;
    String disposition = "inline";

    // If content type is unknown, then set the default value.
    // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
    // To add new content types, add new mime-mapping entry in web.xml.
    if (contentType == null) {
        contentType = "application/octet-stream";
    }

    // If content type is text, then determine whether GZIP content encoding is supported by
    // the browser and expand content type with the one and right character encoding.
    // Else, expect for images, determine content disposition. If content type is supported by
    // the browser, then set to inline, else attachment which will pop a 'save as' dialogue.
    if (contentType.startsWith("text")) {
        String acceptEncoding = request.getHeader("Accept-Encoding");
        acceptsGzip = acceptEncoding != null && accepts(acceptEncoding, "gzip");
        contentType += ";charset=UTF-8";
    } else if (!contentType.startsWith("image")) {
        String accept = request.getHeader("Accept");
        disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment";
    }

    // Initialize response.
    response.reset();
    response.setBufferSize(DEFAULT_BUFFER_SIZE);
    response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\"");
    response.setHeader("Accept-Ranges", "bytes");
    response.setHeader("ETag", eTag);
    response.setDateHeader("Last-Modified", lastModified);
    response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME);

    // Send requested file (part(s)) to client ------------------------------------------------

    // Prepare streams.
    RandomAccessFile input = null;
    OutputStream output = null;

    try {
        // Open streams.
        input = new RandomAccessFile(file, "r");
        output = response.getOutputStream();

        if (ranges.isEmpty() || ranges.get(0) == full) {

            // Return full file.
            Range r = full;
            response.setContentType(contentType);
            response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);

            if (content) {
                if (acceptsGzip) {
                    // The browser accepts GZIP, so GZIP the content.
                    response.setHeader("Content-Encoding", "gzip");
                    output = new GZIPOutputStream(output, DEFAULT_BUFFER_SIZE);
                } else {
                    // Content length is not directly predictable in case of GZIP.
                    // So only add it if there is no means of GZIP, else browser will hang.
                    response.setHeader("Content-Length", String.valueOf(r.length));
                }

                // Copy full range.
                copy(input, output, r.start, r.length);
            }

        } else if (ranges.size() == 1) {

            // Return single part of file.
            Range r = ranges.get(0);
            response.setContentType(contentType);
            response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);
            response.setHeader("Content-Length", String.valueOf(r.length));
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.

            if (content) {
                // Copy single part range.
                copy(input, output, r.start, r.length);
            }

        } else {

            // Return multiple parts of file.
            response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY);
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.

            if (content) {
                // Cast back to ServletOutputStream to get the easy println methods.
                ServletOutputStream sos = (ServletOutputStream) output;

                // Copy multi part range.
                for (Range r : ranges) {
                    // Add multipart boundary and header fields for every range.
                    sos.println();
                    sos.println("--" + MULTIPART_BOUNDARY);
                    sos.println("Content-Type: " + contentType);
                    sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total);

                    // Copy single part range of multi part range.
                    copy(input, output, r.start, r.length);
                }

                // End with multipart boundary.
                sos.println();
                sos.println("--" + MULTIPART_BOUNDARY + "--");
            }
        }
    } finally {
        // Gently close streams.
        close(output);
        close(input);
    }
}

From source file:org.geowebcache.util.ResponseUtils.java

/**
 * Happy ending, sets the headers and writes the response back to the client.
 *//*from   ww  w . j a va 2  s. c  o m*/
private static void writeData(ConveyorTile tile, RuntimeStats runtimeStats) throws IOException {
    HttpServletResponse servletResp = tile.servletResp;
    final HttpServletRequest servletReq = tile.servletReq;

    final CacheResult cacheResult = tile.getCacheResult();
    int httpCode = HttpServletResponse.SC_OK;
    Resource blob = tile.getBlob();
    String mimeType = tile.getMimeType().getMimeType(blob);

    servletResp.setHeader("geowebcache-cache-result", String.valueOf(cacheResult));
    servletResp.setHeader("geowebcache-tile-index", Arrays.toString(tile.getTileIndex()));
    long[] tileIndex = tile.getTileIndex();
    TileLayer layer = tile.getLayer();
    GridSubset gridSubset = layer.getGridSubset(tile.getGridSetId());
    BoundingBox tileBounds = gridSubset.boundsFromIndex(tileIndex);
    servletResp.setHeader("geowebcache-tile-bounds", tileBounds.toString());
    servletResp.setHeader("geowebcache-gridset", gridSubset.getName());
    servletResp.setHeader("geowebcache-crs", gridSubset.getSRS().toString());

    final long tileTimeStamp = tile.getTSCreated();
    final String ifModSinceHeader = servletReq.getHeader("If-Modified-Since");
    // commons-httpclient's DateUtil can encode and decode timestamps formatted as per RFC-1123,
    // which is one of the three formats allowed for Last-Modified and If-Modified-Since headers
    // (e.g. 'Sun, 06 Nov 1994 08:49:37 GMT'). See
    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1

    final String lastModified = org.apache.commons.httpclient.util.DateUtil.formatDate(new Date(tileTimeStamp));
    servletResp.setHeader("Last-Modified", lastModified);

    final Date ifModifiedSince;
    if (ifModSinceHeader != null && ifModSinceHeader.length() > 0) {
        try {
            ifModifiedSince = DateUtil.parseDate(ifModSinceHeader);
            // the HTTP header has second precision
            long ifModSinceSeconds = 1000 * (ifModifiedSince.getTime() / 1000);
            long tileTimeStampSeconds = 1000 * (tileTimeStamp / 1000);
            if (ifModSinceSeconds >= tileTimeStampSeconds) {
                httpCode = HttpServletResponse.SC_NOT_MODIFIED;
                blob = null;
            }
        } catch (DateParseException e) {
            if (log.isDebugEnabled()) {
                log.debug("Can't parse client's If-Modified-Since header: '" + ifModSinceHeader + "'");
            }
        }
    }

    if (httpCode == HttpServletResponse.SC_OK && tile.getLayer().useETags()) {
        String ifNoneMatch = servletReq.getHeader("If-None-Match");
        String hexTag = Long.toHexString(tileTimeStamp);

        if (ifNoneMatch != null) {
            if (ifNoneMatch.equals(hexTag)) {
                httpCode = HttpServletResponse.SC_NOT_MODIFIED;
                blob = null;
            }
        }

        // If we get here, we want ETags but the client did not have the tile.
        servletResp.setHeader("ETag", hexTag);
    }

    int contentLength = (int) (blob == null ? -1 : blob.getSize());
    writeFixedResponse(servletResp, httpCode, mimeType, blob, cacheResult, contentLength, runtimeStats);
}

From source file:org.niord.proxy.web.ETagServletFilter.java

/**
 * Inspired by javax.ws.rs.core.Request.evaluatePreconditions().
 * Evaluate request preconditions based on the passed in value.
 *
 * @param etag an ETag for the current state of the resource
 * @return if the preconditions are met.
 *///  www.ja  v a 2  s  . c  o m
private boolean evaluatePreconditions(EntityTag etag, HttpServletRequest request,
        HttpServletResponse response) {
    if (etag == null) {
        return false;
    }

    response.setHeader(HEADER_ETAG, etag.toString());

    boolean match = Collections.list(request.getHeaders(HEADER_IF_NONE_MATCH)).stream().map(this::trimEtagValue) // Strip any "-gzip" suffix added by Apache modules
            .anyMatch(val -> val.equals(etag.toString()));

    if (match) {
        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        return true;
    }

    // No matching etag headers
    return false;
}

From source file:com.codename1.corsproxy.CORSProxy.java

protected boolean doResponseRedirectOrNotModifiedLogic(HttpServletRequest servletRequest,
        HttpServletResponse servletResponse, HttpResponse proxyResponse, int statusCode)
        throws ServletException, IOException {

    // 304 needs special handling.  See:
    // http://www.ics.uci.edu/pub/ietf/http/rfc1945.html#Code304
    // We get a 304 whenever passed an 'If-Modified-Since'
    // header and the data on disk has not changed; server
    // responds w/ a 304 saying I'm not going to send the
    // body because the file has not changed.
    if (statusCode == HttpServletResponse.SC_NOT_MODIFIED) {
        servletResponse.setIntHeader(HttpHeaders.CONTENT_LENGTH, 0);
        servletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        return true;
    }//www  .j  ava2  s  . c o m
    return false;
}

From source file:eionet.gdem.web.FileDownloadServlet.java

/**
 * Process the actual request.//  w  ww  .j a  v a2 s .c  o  m
 *
 * @param request
 *            The request to be processed.
 * @param response
 *            The response to be created.
 * @param content
 *            Whether the request body should be written (GET) or not (HEAD).
 * @throws IOException
 *             If something fails at I/O level.
 * @throws ServletException If an error occurs.
 */
private void processRequest(HttpServletRequest request, HttpServletResponse response, boolean content)
        throws IOException, ServletException {

    String urlPath = URLDecoder
            .decode(StringUtils.substringAfter(request.getRequestURI(), request.getContextPath()), "UTF-8");
    String filePath = Properties.appRootFolder + urlPath;

    String securityMessage = checkPermissions(request, urlPath);
    if (securityMessage != null) {
        handleNotAuthorised(securityMessage, request, response);
        return;
    }

    // Get the file object from the file store
    File file = new File(filePath);

    // If file was not found, send 404.
    if (file == null || !file.exists() || !file.isFile()) {
        handleFileNotFound("Could not find file by the following URI: " + request.getRequestURI(), request,
                response);
        return;
    }

    // Prepare some variables. The ETag is an unique identifier of the file.
    String fileName = file.getName();
    long length = file.length();
    long lastModified = file.lastModified();
    String eTag = fileName + "_" + length + "_" + lastModified;

    // Validate request headers for caching ---------------------------------------------------

    // If-None-Match header should contain "*" or ETag. If so, then return 304.
    String ifNoneMatch = request.getHeader("If-None-Match");
    if (ifNoneMatch != null && matches(ifNoneMatch, eTag)) {
        response.setHeader("ETag", eTag); // Required in 304.
        response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
        return;
    }

    // If-Modified-Since header should be greater than LastModified. If so, then return 304.
    // This header is ignored if any If-None-Match header is specified.
    long ifModifiedSince = request.getDateHeader("If-Modified-Since");
    if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
        response.setHeader("ETag", eTag); // Required in 304.
        response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
        return;
    }

    // Validate request headers for resume ----------------------------------------------------

    // If-Match header should contain "*" or ETag. If not, then return 412.
    String ifMatch = request.getHeader("If-Match");
    if (ifMatch != null && !matches(ifMatch, eTag)) {
        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
        return;
    }

    // If-Unmodified-Since header should be greater than LastModified. If not, then return 412.
    long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since");
    if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) {
        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
        return;
    }

    // Validate and process range -------------------------------------------------------------

    // Prepare some variables. The full Range represents the complete file.
    Range full = new Range(0, length - 1, length);
    List<Range> ranges = new ArrayList<Range>();

    // Validate and process Range and If-Range headers.
    String range = request.getHeader("Range");
    if (range != null) {

        // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416.
        if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) {
            response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return;
        }

        // If-Range header should either match ETag or be greater then LastModified. If not,
        // then return full file.
        String ifRange = request.getHeader("If-Range");
        if (ifRange != null && !ifRange.equals(eTag)) {
            try {
                long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid.
                if (ifRangeTime != -1 && ifRangeTime + 1000 < lastModified) {
                    ranges.add(full);
                }
            } catch (IllegalArgumentException ignore) {
                ranges.add(full);
            }
        }

        // If any valid If-Range header, then process each part of byte range.
        if (ranges.isEmpty()) {
            for (String part : range.substring(6).split(",")) {
                // Assuming a file with length of 100, the following examples returns bytes at:
                // 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100).
                long start = sublong(part, 0, part.indexOf("-"));
                long end = sublong(part, part.indexOf("-") + 1, part.length());

                if (start == -1) {
                    start = length - end;
                    end = length - 1;
                } else if (end == -1 || end > length - 1) {
                    end = length - 1;
                }

                // Check if Range is syntactically valid. If not, then return 416.
                if (start > end) {
                    response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
                    response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                    return;
                }

                // Add range.
                ranges.add(new Range(start, end, length));
            }
        }
    }

    // Prepare and initialize response --------------------------------------------------------

    // Get content type by file name and set default GZIP support and content disposition.
    String contentType = getServletContext().getMimeType(fileName);
    boolean acceptsGzip = false;
    String disposition = "inline";

    // If content type is unknown, then set the default value.
    // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
    // To add new content types, add new mime-mapping entry in web.xml.
    if (contentType == null) {
        contentType = "text/plain";
    }

    // If content type is text, then determine whether GZIP content encoding is supported by
    // the browser and expand content type with the one and right character encoding.
    // Else, expect for images, determine content disposition. If content type is supported by
    // the browser, then set to inline, else attachment which will pop a 'save as' dialogue.
    if (contentType.startsWith("text")) {
        String acceptEncoding = request.getHeader("Accept-Encoding");
        acceptsGzip = acceptEncoding != null && accepts(acceptEncoding, "gzip");
        contentType += ";charset=UTF-8";
    } else if (!contentType.startsWith("image")) {
        String accept = request.getHeader("Accept");
        disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment";
    }

    // Initialize response.
    response.reset();
    response.setBufferSize(DEFAULT_BUFFER_SIZE);
    response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\"");
    response.setHeader("Accept-Ranges", "bytes");
    response.setHeader("ETag", eTag);
    response.setDateHeader("Last-Modified", lastModified);
    response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME);

    // Send requested file (part(s)) to client ------------------------------------------------

    // Prepare streams.
    RandomAccessFile input = null;
    OutputStream output = null;

    try {
        // Open streams.
        input = new RandomAccessFile(file, "r");
        output = response.getOutputStream();

        if (ranges.isEmpty() || ranges.get(0) == full) {

            // Return full file.
            Range r = full;
            response.setContentType(contentType);
            response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);

            if (content) {
                if (acceptsGzip) {
                    // The browser accepts GZIP, so GZIP the content.
                    response.setHeader("Content-Encoding", "gzip");
                    output = new GZIPOutputStream(output, DEFAULT_BUFFER_SIZE);
                } else {
                    // Content length is not directly predictable in case of GZIP.
                    // So only add it if there is no means of GZIP, else browser will hang.
                    response.setHeader("Content-Length", String.valueOf(r.length));
                }

                // Copy full range.
                copy(input, output, r.start, r.length);
            }

        } else if (ranges.size() == 1) {

            // Return single part of file.
            Range r = ranges.get(0);
            response.setContentType(contentType);
            response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);
            response.setHeader("Content-Length", String.valueOf(r.length));
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.

            if (content) {
                // Copy single part range.
                copy(input, output, r.start, r.length);
            }

        } else {

            // Return multiple parts of file.
            response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY);
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.

            if (content) {
                // Cast back to ServletOutputStream to get the easy println methods.
                ServletOutputStream sos = (ServletOutputStream) output;

                // Copy multi part range.
                for (Range r : ranges) {
                    // Add multipart boundary and header fields for every range.
                    sos.println();
                    sos.println("--" + MULTIPART_BOUNDARY);
                    sos.println("Content-Type: " + contentType);
                    sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total);

                    // Copy single part range of multi part range.
                    copy(input, output, r.start, r.length);
                }

                // End with multipart boundary.
                sos.println();
                sos.println("--" + MULTIPART_BOUNDARY + "--");
            }
        }
    } finally {
        // Gently close streams.
        close(output);
        close(input);
    }
}

From source file:org.jtalks.jcommune.web.controller.AvatarController.java

/**
 * Write user avatar in response for rendering it on html pages.
 *
 * @param request  servlet request//from   ww  w .ja va2 s .c  om
 * @param response servlet response
 * @param id       user database identifier
 * @throws NotFoundException if user with given encodedUsername not found
 * @throws IOException       throws if an output exception occurred
 */
@RequestMapping(value = "/users/{id}/avatar", method = RequestMethod.GET)
public void renderAvatar(HttpServletRequest request, HttpServletResponse response, @PathVariable Long id)
        throws NotFoundException, IOException {
    JCUser user = userService.get(id);

    Date ifModifiedDate = super.getIfModifiedSinceDate(request.getHeader(IF_MODIFIED_SINCE_HEADER));
    // using 0 unix time if avatar has never changed (the date is null). It's easier to work with something
    // non-null than to check for null all the time.
    DateTime avatarLastModificationTime = defaultIfNull(user.getAvatarLastModificationTime(), new DateTime(0));
    // if-modified-since header doesn't include milliseconds, so if it floors the value (millis = 0), then
    // actual modification date will always be after the if-modified-since and we'll always be returning avatar
    avatarLastModificationTime = avatarLastModificationTime.withMillisOfSecond(0);
    if (avatarLastModificationTime.isAfter(ifModifiedDate.getTime())) {
        byte[] avatar = user.getAvatar();
        response.setContentType("image/jpeg");
        response.setContentLength(avatar.length);
        response.getOutputStream().write(avatar);
    } else {
        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
    }
    setupAvatarHeaders(response, new Date(avatarLastModificationTime.getMillis()));
}