Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * (C) Copyright 2013 Kurento (http://kurento.org/)
    *
    * All rights reserved. This program and the accompanying materials
    * are made available under the terms of the GNU Lesser General Public License
    * (LGPL) version 2.1 which accompanies this distribution, and is available at
    * http://www.gnu.org/licenses/lgpl-2.1.html
    *
    * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   * Lesser General Public License for more details.
   *
   */
  
  package com.kurento.kmf.repository.internal.http;
  
  import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
  import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
  import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
  import static javax.servlet.http.HttpServletResponse.SC_OK;
  import static javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT;
  import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED;
  import static javax.servlet.http.HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE;
  
  import java.io.File;
  import java.util.List;
  
  
  import org.slf4j.Logger;
  import  org.springframework.beans.factory.annotation.Autowired;
  
  
  @WebServlet(value = "/repository_servlet/*", loadOnStartup = 1)
  public class RepositoryHttpServlet extends HttpServlet {
  
  	protected static class Range {
  
  		public long start;
  		public long end;
  		public long length;

Validate range.
  
  		public boolean validate() {
  			if ( != -1 &&  >= ) {
  				 =  - 1;
  			}
  			return ( >= 0) && ( >= 0) && ( <= )
  					&& ( == -1 ||  > 0);
  		}
  	}
  
  	private static Logger log = LoggerFactory
  
  	private static final long serialVersionUID = 1L;

Full range constant.
  
  	protected static final List<RangeFULL = new ArrayList<>();

MIME multipart separation string
  
  	protected static final String MIME_SEPARATION = "KURENTO_MIME_BOUNDARY";

Size of file transfer buffer in bytes.
 
 	protected static final int FILE_BUFFER_SIZE = 4096;

The input buffer size to use when serving resources.
 
 	private static final int INPUT_BUFFER_SIZE = 2048;

The output buffer size to use when serving resources.
 
 	private static final int OUTPUT_BUFFER_SIZE = 2048;

The debugging detail level for this servlet.
 
 	protected int debug;

RepoItemHttpElems
 
 	@Autowired
 	protected transient RepositoryHttpManager repoHttpManager;
 
 	@Autowired
Finalize this servlet.
 
 	public void destroy() {
 		// NOOP
 	}

Initialize this servlet.
 
 	public void init(ServletConfig servletConfigthrows ServletException {
 
 		super.init(servletConfig);
 
 		configureServletMapping(servletConfig);
 		configureWebappPublicURL(servletConfig);
 
 		if (servletConfig.getInitParameter("debug") != null) {
 					"debug"));
 		}
 	}
 
 	private String configureWebappPublicURL(ServletConfig servletConfig) {
 		String webappURL = .getWebappPublicURL();
 		if (webappURL == null || webappURL.trim().isEmpty()) {
 			webappURL = servletConfig.getServletContext().getContextPath();
 		} else {
 			if (webappURL.endsWith("/")) {
 				webappURL = webappURL.substring(0, webappURL.length() - 1);
 			}
 		}
 
 		return webappURL;
 	}
 
 	private String configureServletMapping(ServletConfig servletConfig) {
 		Collection<Stringmappings = servletConfig.getServletContext()
 
 		if (mappings.isEmpty()) {
 			throw new KurentoException("There is no mapping for servlet "
 		}
 
 		String mapping = mappings.iterator().next();
 
 		// TODO: Document this. We assume a mapping starting with / and ending
 		// with /*
 		mapping = mapping.substring(0, mapping.length() - 1);
 
 
 		return mapping;
 	}
 
 	private void configureKurentoAppContext(ServletConfig servletConfig) {
 
 		if (KurentoApplicationContextUtils.getKurentoApplicationContext() == null) {
 			KurentoApplicationContextUtils
 		}
 
 		KurentoApplicationContextUtils
 	}
 
 	protected void service(HttpServletRequest reqHttpServletResponse resp)
 		logRequest(req);
 		super.service(reqresp);
 		logResponse(resp);
 	}

Override default implementation to ensure that TRACE is correctly handled.

Parameters:
req the HttpServletRequest object that contains the request the client made of the servlet
resp the HttpServletResponse object that contains the response the servlet returns to the client
Throws:
IOException if an input or output error occurs while the servlet is handling the OPTIONS request
ServletException if the request for the OPTIONS cannot be handled
 
 	protected void doOptions(HttpServletRequest reqHttpServletResponse resp)
 
 		resp.setHeader("Allow""GET, HEAD, POST, PUT, OPTIONS");
 	}

Process a HEAD request for the specified resource.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
Throws:
IOException if an input/output error occurs
ServletException if a servlet-specified error occurs
 
 	protected void doHead(HttpServletRequest request,
 
 		// Serve the requested resource, without the data content
 		serveResource(requestresponsefalse);
 
 	}

Process a POST request for the specified resource.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
Throws:
IOException if an input/output error occurs
ServletException if a servlet-specified error occurs
 
 	protected void doPost(HttpServletRequest request,
 		doPut(requestresponse);
 	}

Process a PUT request for the specified resource.

Parameters:
req The servlet request we are processing
resp The servlet response we are creating
Throws:
IOException if an input/output error occurs
ServletException if a servlet-specified error occurs
 
 	protected void doPut(HttpServletRequest reqHttpServletResponse resp)
 		uploadContent(reqresp);
 	}

Process a GET request for the specified resource.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
Throws:
IOException if an input/output error occurs
ServletException if a servlet-specified error occurs
 
 	protected void doGet(HttpServletRequest request,
 		serveResource(requestresponsetrue);
 	}
 
 	protected void uploadContent(HttpServletRequest req,
 			HttpServletResponse respthrows IOException {
 
 		String sessionId = extractSessionId(req);
 
 				.getHttpRepoItemElem(sessionId);
 
 		if (elem == null) {
 			return;
 		}
 
 
 		try (InputStream requestInputStream = req.getInputStream()) {
 
 			try (OutputStream repoItemOutputStream = elem
 
 				Range range = parseContentRange(reqresp);
 
 				if (range != null) {
 
 					if (range.start > elem.getWrittenBytes()) {
 								"The server doesn't support writing ranges "
 										+ "ahead of previously written bytes");
 					} else if (range.end == elem.getWrittenBytes()) {
 
 						// TODO We assume that the put range is the same than
 						// the
 						// previous one. Do we need to check this?
 
 										"The server has detected that the submited range "
 												+ "has already submited in a previous request");
 					} else if (range.start < elem.getWrittenBytes()
 							&& range.end > elem.getWrittenBytes()) {
 
 						Range copyRange = new Range();
 						copyRange.start = elem.getWrittenBytes() - range.start;
 						copyRange.end = range.end - range.start;
 
 						copyStreamsRange(requestInputStream,
 								repoItemOutputStreamcopyRange);
 
 
 					} else if (range.start == elem.getWrittenBytes()) {
 
 						IOUtils.copy(requestInputStreamrepoItemOutputStream);
 
 
 					}
 
 				} else {
 
 					boolean isMultipart = ServletFileUpload
 
 					if (isMultipart) {
 
 						uploadMultipart(reqresprepoItemOutputStream);
 
 					} else {
 
 						try {
 
 							.info("Start to receive bytes (estimated "
 									+ req.getContentLength() + " bytes)");
 							int bytes = IOUtils.copy(requestInputStream,
 									repoItemOutputStream);
 							.info("Bytes received: " + bytes);
 
 						} catch (Exception e) {
 
 							.warn("Exception when uploading content"e);
 
 						}
 					}
 				}
 			}
 
 		} finally {
 			elem.stopInTimeout();
 		}
 	}
 
 	private void uploadMultipart(HttpServletRequest req,
 			HttpServletResponse respOutputStream repoItemOutputStrem)
 			throws IOException {
 
 		.info("Multipart detected");
 
 
 		try {
 
 			// Parse the request
 			FileItemIterator iter = upload.getItemIterator(req);
 			while (iter.hasNext()) {
 				FileItemStream item = iter.next();
 				String name = item.getFieldName();
 				try (InputStream stream = item.openStream()) {
 					if (item.isFormField()) {
 						// TODO What to do with this?
 						.info("Form field {} with value {} detected."name,
 								Streams.asString(stream));
 					} else {
 
 						// TODO Must we support multiple files uploading?
 						.info("File field {} with file name detected.",
 								nameitem.getName());
 
 						.info("Start to receive bytes (estimated bytes)",
 								Integer.toString(req.getContentLength()));
 						int bytes = IOUtils.copy(streamrepoItemOutputStrem);
 						.info("Bytes received: {}", Integer.toString(bytes));
 					}
 				}
 			}
 
 		} catch (FileUploadException e) {
 			throw new IOException(e);
 		}
 	}
 
 	private void logRequest(HttpServletRequest req) {
 		.info("Request received " + req.getRequestURL());
 		.info("  Method: " + req.getMethod());
 
 		Enumeration<StringheaderNames = req.getHeaderNames();
 		while (headerNames.hasMoreElements()) {
 
 			String headerName = headerNames.nextElement();
 			Enumeration<Stringvalues = req.getHeaders(headerName);
 			List<StringvalueList = new ArrayList<>();
 			while (values.hasMoreElements()) {
 				valueList.add(values.nextElement());
 			}
 			.info("  Header {}: {}"headerNamevalueList);
 		}
 	}
 
 	private void logResponse(HttpServletResponse resp) {
 
 		Collection<StringheaderNames = resp.getHeaderNames();
 		for (String headerName : headerNames) {
 			Collection<Stringvalues = resp.getHeaders(headerName);
 			.info("  Header {}: {}"headerNamevalues);
 		}
 	}

Return the sessionId from the request.

Parameters:
request The servlet request we are processing
 
 	protected String extractSessionId(HttpServletRequest request) {
 
 		// Path info without leading "/"
 		String pathInfo = request.getPathInfo();
 
 		if (pathInfo != null && pathInfo.length() >= 1) {
 			return pathInfo.substring(1);
 		}
 
 		return null;
 
 	}

Handle a partial PUT. New content specified in request is appended to existing content in oldRevisionContent (if present). This code does not support simultaneous partial updates to the same resource.
 
 	protected File executePartialPut(HttpServletRequest reqRange range,
 			String sessionIdthrows IOException {
 
 		// TODO: Change this implementation to avoid Files. Try to
 		// make the work on the repository implementation.
 
 		// Append data specified in ranges to existing content for this
 		// resource - create a temp. file on the local filesystem to
 		// perform this operation
 		// Assume just one range is specified for now
 
 
 		// Convert all '/' characters to '.' in resourcePath
 		String convertedResourcePath = sessionId.replace('/''.');
 
 		File contentFile = new File(tempDirconvertedResourcePath);
 
 		if (contentFile.createNewFile()) {
 			// Clean up contentFile when Tomcat is terminated
 			contentFile.deleteOnExit();
 		}
 
 		try (RandomAccessFile randAccessContentFile = new RandomAccessFile(
 				contentFile"rw")) {
 
 					.getHttpRepoItemElem(sessionId);
 
 			// Copy data in oldRevisionContent to contentFile
 			if (repoItemHttpElem != null) {
 
 				try (BufferedInputStream bufOldRevStream = new BufferedInputStream(
 						repoItemHttpElem.createRepoItemInputStream(),
 
 					int numBytesRead;
 					byte[] copyBuffer = new byte[];
 					while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) {
 						randAccessContentFile
 								.write(copyBuffer, 0, numBytesRead);
 					}
 				}
 			}
 
 			randAccessContentFile.setLength(range.length);
 
 			// Append data in request input stream to contentFile
 			randAccessContentFile.seek(range.start);
 
 			int numBytesRead;
 
 			byte[] transferBuffer = new byte[];
 
 			try (BufferedInputStream requestBufInStream = new BufferedInputStream(
 
 				while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) {
 					randAccessContentFile
 							.write(transferBuffer, 0, numBytesRead);
 				}
 			}
 		}
 
 		return contentFile;
 	}

Check if the conditions specified in the optional If headers are satisfied.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
resourceAttributes The resource information
Returns:
boolean true if the resource meets all the specified conditions, and false if any of the conditions is not satisfied, in which case request processing is stopped
 
 	protected boolean checkIfHeaders(HttpServletRequest request,
 			RepositoryItemAttributes resourceAttributesthrows IOException {
 
 		// TODO Investigate how to load properties for RepositoryItem (Mongo or
 		// Filesystem)
 
 		return checkIfMatch(requestresponseresourceAttributes)
 				&& checkIfModifiedSince(requestresponseresourceAttributes)
 				&& checkIfNoneMatch(requestresponseresourceAttributes)
 				&& checkIfUnmodifiedSince(requestresponseresourceAttributes);
 	}

Serve the specified resource, optionally including the data content.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
content Should the content be included?
Throws:
IOException if an input/output error occurs
ServletException if a servlet-specified error occurs
 
 	protected void serveResource(HttpServletRequest request,
 			HttpServletResponse responseboolean contentthrows IOException,
 
 		boolean serveContent = content;
 
 		// Identify the requested resource path
 		String sessionId = extractSessionId(request);
 
 				.getHttpRepoItemElem(sessionId);
 
 		if (elem == null) {
 
 			if ( > 0) {
 				log("Resource with sessionId '" + sessionId + "' not found");
 			}
 
 			response.sendError(request.getRequestURI());
 			return;
 		}
 
 
 		RepositoryItem repositoryItem = elem.getRepositoryItem();
 		RepositoryItemAttributes attributes = repositoryItem.getAttributes();
 
 		if ( > 0) {
 			if (serveContent) {
 				log("Serving resource with sessionId '"
 						+ sessionId
 						+ "' headers and data. This resource corresponds to repository item '"
 						+ repositoryItem.getId() + "'");
 			} else {
 				log("Serving resource with sessionId '"
 						+ sessionId
 						+ "' headers only. This resource corresponds to repository item '"
 						+ repositoryItem.getId() + "'");
 			}
 		}
 
 		boolean malformedRequest = response.getStatus() >= ;
 
 		if (!malformedRequest && !checkIfHeaders(requestresponseattributes)) {
 			return;
 		}
 
 		String contentType = getContentType(elemattributes);
 
 		List<Rangeranges = null;
 
 		if (!malformedRequest) {
 
 			response.setHeader("Accept-Ranges""bytes");
 			response.setHeader("ETag"attributes.getETag());
 			response.setHeader("Last-Modified",
 					attributes.getLastModifiedHttp());
 
 			ranges = parseRange(requestresponseattributes);
 		}
 
 		long contentLength = attributes.getContentLength();
 
 		// Special case for zero length files, which would cause a
 		// (silent) ISE when setting the output buffer size
 		if (contentLength == 0L) {
 			serveContent = false;
 		}
 
 		// Check to see if a Filter, Valve of wrapper has written some content.
 		// If it has, disable range requests and setting of a content length
 		// since neither can be done reliably.
 		boolean contentWritten = response.isCommitted();
 
 		if (contentWritten) {
 			ranges = ;
 		}
 
 		boolean noRanges = (ranges == null || ranges.isEmpty());
 
 		if (malformedRequest
 				|| (noRanges && request.getHeader("Range") == null)
 				|| ranges == ) {
 
 			setContentType(responsecontentType);
 
 			if (contentLength >= 0) {
 				// Don't set a content length if something else has already
 				// written to the response.
 				if (!contentWritten) {
 					setContentLength(responsecontentLength);
 				}
 			}
 
 			// Copy the input stream to our output stream (if requested)
 			if (serveContent) {
 				copy(elemresponse);
 			}
 
 		} else {
 
 			if (noRanges) {
 				return;
 			}
 
 			// Partial content response.
 
 			if (ranges.size() == 1) {
 
 				Range range = ranges.get(0);
 
 				response.addHeader("Content-Range""bytes " + range.start
 						+ "-" + range.end + "/" + range.length);
 
 				long length = range.end - range.start + 1;
 
 				setContentLength(responselength);
 				setContentType(responsecontentType);
 
 				if (serveContent) {
 					copy(elemresponserange);
 				}
 
 			} else {
 
 				response.setContentType("multipart/byteranges; boundary="
 
 				if (serveContent) {
 					copy(elemresponserangescontentType);
 				}
 			}
 		}
 
 		elem.stopInTimeout();
 
 	}
 
 	private String getContentType(RepositoryHttpEndpointImpl repoItemHttpElem,
 			RepositoryItemAttributes attributes) {
 
 		String contentType = attributes.getMimeType();
 		if (contentType == null) {
 			contentType = getServletContext().getMimeType(
 					repoItemHttpElem.getRepositoryItem().getId());
 			attributes.setMimeType(contentType);
 		}
 		return contentType;
 	}
 
 	private void setContentType(HttpServletResponse responseString contentType) {
 		if (contentType != null) {
 			if ( > 0) {
 				log("contentType='" + contentType + "'");
 			}
 			response.setContentType(contentType);
 		}
 	}
 
 	private void setContentLength(HttpServletResponse responselong length) {
 		if ( > 0) {
 			log("contentLength=" + length);
 		}
 
 		if (length < .) {
 			response.setContentLength((intlength);
 		} else {
 			// Set the content-length as String to be able to use a long
 			response.setHeader("content-length""" + length);
 		}
 	}

Parse the content-range header.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
Returns:
Range
 
 			HttpServletResponse responsethrows IOException {
 
 		// Retrieving the content-range header (if any is specified
 		String rangeHeader = request.getHeader("Content-Range");
 
 		if (rangeHeader == null) {
 			return null;
 		}
 
 		// bytes is the only range unit supported
 		if (!rangeHeader.startsWith("bytes")) {
 			return null;
 		}
 
 		rangeHeader = rangeHeader.substring(6).trim();
 
 		int dashPos = rangeHeader.indexOf('-');
 		int slashPos = rangeHeader.indexOf('/');
 
 		if (dashPos == -1) {
 			return null;
 		}
 
 		if (slashPos == -1) {
 			return null;
 		}
 
 		Range range = new Range();
 
 		try {
 			range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
 			range.end = Long.parseLong(rangeHeader.substring(dashPos + 1,
 					slashPos));
 
 			String lengthString = rangeHeader.substring(slashPos + 1,
 					rangeHeader.length());
 
 			if (lengthString.equals("*")) {
 				range.length = -1;
 			} else {
 				range.length = Long.parseLong(lengthString);
 			}
 
 		} catch (NumberFormatException e) {
 			return null;
 		}
 
 		if (!range.validate()) {
 			return null;
 		}
 
 		return range;
 	}

Parse the range header.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
Returns:
Vector of ranges
 
 	protected List<RangeparseRange(HttpServletRequest request,
 			RepositoryItemAttributes resourceAttributesthrows IOException {
 
 		// Checking If-Range
 		String headerValue = request.getHeader("If-Range");
 
 		if (headerValue != null) {
 
 			long headerValueTime = -1L;
 			try {
 				headerValueTime = request.getDateHeader("If-Range");
 			} catch (IllegalArgumentException e) {
 				// Ignore
 			}
 
 			String eTag = resourceAttributes.getETag();
 			long lastModified = resourceAttributes.getLastModified();
 
 			if (headerValueTime == -1L) {
 
 				// If the ETag the client gave does not match the entity
 				// etag, then the entire entity is returned.
 				if (!eTag.equals(headerValue.trim())) {
 					return ;
 				}
 
 			} else {
 
 				// If the timestamp of the entity the client got is older than
 				// the last modification date of the entity, the entire entity
 				// is returned.
 				if (lastModified > (headerValueTime + 1000)) {
 					return ;
 				}
 
 			}
 		}
 
 		long fileLength = resourceAttributes.getContentLength();
 
 		if (fileLength == 0) {
 			return null;
 		}
 
 		// Retrieving the range header (if any is specified
 		String rangeHeader = request.getHeader("Range");
 
 		if (rangeHeader == null) {
 			return null;
 		}
 		// bytes is the only range unit supported (and I don't see the point
 		// of adding new ones).
 		if (!rangeHeader.startsWith("bytes")) {
 			response.addHeader("Content-Range""bytes */" + fileLength);
 			return null;
 		}
 
 		rangeHeader = rangeHeader.substring(6);
 
 		// Vector which will contain all the ranges which are successfully
 		// parsed.
 		List<Rangeresult = new ArrayList<>();
 		StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader",");
 
 		// Parsing the range list
 		while (commaTokenizer.hasMoreTokens()) {
 
 			String rangeDefinition = commaTokenizer.nextToken().trim();
 
 			Range currentRange = new Range();
 			currentRange.length = fileLength;
 
 			int dashPos = rangeDefinition.indexOf('-');
 
 			if (dashPos == -1) {
 				response.addHeader("Content-Range""bytes */" + fileLength);
 				return null;
 			}
 
 			if (dashPos == 0) {
 
 				try {
 					long offset = Long.parseLong(rangeDefinition);
 					currentRange.start = fileLength + offset;
 					currentRange.end = fileLength - 1;
 				} catch (NumberFormatException e) {
 					response.addHeader("Content-Range""bytes */" + fileLength);
 					return null;
 				}
 
 			} else {
 
 				try {
 					currentRange.start = Long.parseLong(rangeDefinition
 							.substring(0, dashPos));
 					if (dashPos < rangeDefinition.length() - 1) {
 						currentRange.end = Long.parseLong(rangeDefinition
 								.substring(dashPos + 1,
 										rangeDefinition.length()));
 					} else {
 						currentRange.end = fileLength - 1;
 					}
 				} catch (NumberFormatException e) {
 					response.addHeader("Content-Range""bytes */" + fileLength);
 					return null;
 				}
 
 			}
 
 			if (!currentRange.validate()) {
 				response.addHeader("Content-Range""bytes */" + fileLength);
 				return null;
 			}
 
 			result.add(currentRange);
 		}
 
 		return result;
 	}

Check if the if-match condition is satisfied.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
resourceAttributes File object
Returns:
boolean true if the resource meets the specified condition, and false if the condition is not satisfied, in which case request processing is stopped
	protected boolean checkIfMatch(HttpServletRequest request,
			RepositoryItemAttributes resourceAttributesthrows IOException {
		String eTag = resourceAttributes.getETag();
		String headerValue = request.getHeader("If-Match");
		if (headerValue != null) {
			if (headerValue.indexOf('*') == -1) {
				StringTokenizer commaTokenizer = new StringTokenizer(
						headerValue",");
				boolean conditionSatisfied = false;
				while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
					String currentToken = commaTokenizer.nextToken();
					if (currentToken.trim().equals(eTag)) {
						conditionSatisfied = true;
				// If none of the given ETags match, 412 Precodition failed is
				// sent back
				if (!conditionSatisfied) {
					return false;
		return true;
	}

Check if the if-modified-since condition is satisfied.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
resourceAttributes File object
Returns:
boolean true if the resource meets the specified condition, and false if the condition is not satisfied, in which case request processing is stopped
	protected boolean checkIfModifiedSince(HttpServletRequest request,
			RepositoryItemAttributes resourceAttributes) {
		try {
			long headerValue = request.getDateHeader("If-Modified-Since");
			long lastModified = resourceAttributes.getLastModified();
			if (headerValue != -1) {
				// If an If-None-Match header has been specified, if modified
				// since
				// is ignored.
				if ((request.getHeader("If-None-Match") == null)
						&& (lastModified < headerValue + 1000)) {
					// The entity has not been modified since the date
					// specified by the client. This is not an error case.
					response.setHeader("ETag"resourceAttributes.getETag());
					return false;
catch (IllegalArgumentException illegalArgument) {
			return true;
		return true;
	}

Check if the if-none-match condition is satisfied.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
resourceAttributes File object
Returns:
boolean true if the resource meets the specified condition, and false if the condition is not satisfied, in which case request processing is stopped
	protected boolean checkIfNoneMatch(HttpServletRequest request,
			RepositoryItemAttributes resourceAttributesthrows IOException {
		String eTag = resourceAttributes.getETag();
		String headerValue = request.getHeader("If-None-Match");
		if (headerValue != null) {
			boolean conditionSatisfied = false;
			if (!headerValue.equals("*")) {
				StringTokenizer commaTokenizer = new StringTokenizer(
						headerValue",");
				while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
					String currentToken = commaTokenizer.nextToken();
					if (currentToken.trim().equals(eTag)) {
						conditionSatisfied = true;
else {
				conditionSatisfied = true;
			if (conditionSatisfied) {
				// For GET and HEAD, we should respond with
				// 304 Not Modified.
				// For every other method, 412 Precondition Failed is sent
				// back.
				if (("GET".equals(request.getMethod()))
						|| ("HEAD".equals(request.getMethod()))) {
					response.setHeader("ETag"eTag);
					return false;
				return false;
		return true;
	}

Check if the if-unmodified-since condition is satisfied.

Parameters:
request The servlet request we are processing
response The servlet response we are creating
resourceAttributes File object
Returns:
boolean true if the resource meets the specified condition, and false if the condition is not satisfied, in which case request processing is stopped
	protected boolean checkIfUnmodifiedSince(HttpServletRequest request,
			RepositoryItemAttributes resourceAttributesthrows IOException {
		try {
			long lastModified = resourceAttributes.getLastModified();
			long headerValue = request.getDateHeader("If-Unmodified-Since");
			if (headerValue != -1) {
				if (lastModified >= (headerValue + 1000)) {
					// The entity has not been modified since the date
					// specified by the client. This is not an error case.
					return false;
catch (IllegalArgumentException illegalArgument) {
			return true;
		return true;
	}

Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are closed before returning (even in the face of an exception).

Parameters:
repoItemHttpElem The cache entry for the source resource
response The HttpResponse where the resource will be copied
Throws:
IOException if an input/output error occurs
	protected void copy(RepositoryHttpEndpointImpl repoItemHttpElem,
			HttpServletResponse responsethrows IOException {
		copy(repoItemHttpElemresponsenull);
	}

Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are closed before returning (even in the face of an exception).

Parameters:
repoItemHttpElem The cache entry for the source resource
response The response we are writing to
range Range asked by the client
Throws:
IOException if an input/output error occurs
	protected void copy(RepositoryHttpEndpointImpl repoItemHttpElem,
			HttpServletResponse responseRange rangethrows IOException {
		try {
catch (IllegalStateException e) {
			// Silent catch
		IOException exception;
		try (ServletOutputStream ostream = response.getOutputStream()) {
			try (InputStream istream = new BufferedInputStream(
					repoItemHttpElem.createRepoItemInputStream(),
				if (range != null) {
					exception = copyStreamsRange(istreamostreamrange);
else {
					exception = copyStreams(istreamostream);
		// Rethrow any exception that has occurred
		if (exception != null) {
			throw exception;
	}

Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are closed before returning (even in the face of an exception).

Parameters:
repoItemHttpElem The cache entry for the source resource
response The response we are writing to
ranges Enumeration of the ranges the client wanted to retrieve
contentType Content type of the resource
Throws:
IOException if an input/output error occurs
	protected void copy(RepositoryHttpEndpointImpl repoItemHttpElem,
			HttpServletResponse responseList<RangerangesString contentType)
			throws IOException {
		try {
catch (IllegalStateException e) {
			// Silent catch
		IOException exception = null;
		try (ServletOutputStream ostream = response.getOutputStream()) {
			for (Range currentRange : ranges) {
				try (InputStream istream = new BufferedInputStream(
						repoItemHttpElem.createRepoItemInputStream(),
					// Writing MIME header.
					ostream.println();
					ostream.println("--" + );
					if (contentType != null) {
						ostream.println("Content-Type: " + contentType);
					ostream.println("Content-Range: bytes "
currentRange.start + "-" + currentRange.end + "/"
currentRange.length);
					ostream.println();
					exception = copyStreamsRange(istreamostreamcurrentRange);
					if (exception != null) {
						break;
			ostream.println();
			ostream.print("--" +  + "--");
		// Rethrow any exception that has occurred
		if (exception != null) {
			throw exception;
	}

Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are closed before returning (even in the face of an exception).

Parameters:
istream The input stream to read from
ostream The output stream to write to
Returns:
Exception which occurred during processing
	protected IOException copyStreams(InputStream istreamOutputStream ostream) {
		// Copy the input stream to the output stream
		IOException exception = null;
		byte buffer[] = new byte[];
		int len = buffer.length;
		while (true) {
			try {
				len = istream.read(buffer);
				if (len == -1) {
					break;
				ostream.write(buffer, 0, len);
				.debug("{} bytes have been written to item" + len);
catch (IOException e) {
				exception = e;
				len = -1;
				break;
		return exception;
	}

Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are closed before returning (even in the face of an exception).

Parameters:
istream The input stream to read from
ostream The output stream to write to
range Range we are copying
Returns:
Exception which occurred during processing
			OutputStream ostreamRange range) {
		long start = range.start;
		long end = range.end;
		if ( > 10) {
			log("Serving bytes:" + start + "-" + end);
		long skipped = 0;
		try {
			skipped = istream.skip(start);
catch (IOException e) {
			return e;
		if (skipped < start) {
			return new IOException("Has been skiped " + skipped + " when "
start + " is required");
		IOException exception = null;
		long remBytes = end - start + 1;
		byte buffer[] = new byte[];
		int readBytes = buffer.length;
		while (remBytes > 0) {
			try {
				readBytes = istream.read(buffer);
				if (readBytes == -1) {
					break;
else if (readBytes <= remBytes) {
					ostream.write(buffer, 0, readBytes);
					remBytes -= readBytes;
else {
					ostream.write(buffer, 0, (intremBytes);
					break;
catch (IOException e) {
				exception = e;
				break;
		return exception;