Java tutorial
package de.fhg.iais.cortex.rest.resources; /****************************************************************************** * Copyright 2011 (c) Fraunhofer IAIS Netmedia http://www.iais.fraunhofer.de * * ************************************************************************** * * Licensed under the Apache License, Version 2.0 (the "License"); you may * * not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * * software distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * ******************************************************************************/ import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; import java.util.List; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; import com.google.common.io.LimitInputStream; import com.google.inject.Provider; import com.google.inject.name.Named; import de.fhg.iais.commons.annotation.UsedBy; import de.fhg.iais.commons.exception.ParseException; import de.fhg.iais.cortex.rest.resources.authentication.AbstractSecureResource; import de.fhg.iais.cortex.services.IAccessService; import de.fhg.iais.cortex.services.ISearchService; import de.fhg.iais.cortex.services.authentication.annotation.UseAuthentication; import de.fhg.iais.cortex.services.authentication.types.Role; import de.fhg.iais.cortex.services.authentication.types.Role.Profile; import de.fhg.iais.cortex.storage.Binary; import de.fhg.iais.cortex.storage.exception.ItemNotFoundException; import eu.medsea.mimeutil.MimeType; import eu.medsea.mimeutil.MimeUtil2; @Path("/binary") @UsedBy("rest" + "guice") public class BinaryResource extends AbstractSecureResource { private static final Logger LOG = LoggerFactory.getLogger(BinaryResource.class); private final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z"); private final HashFunction hf; private final long ONE_YEAR_IN_MS = 1000L * 60L * 60L * 24L * 365L; private final IAccessService accessService; private final MimeUtil2 mimeUtil; @Inject // public BinaryResource(IAccessService accessService, Provider<Role> roleProvider, @Named("auth.aas.blacklistedProvider") String blacklist) { // super(accessService, roleProvider, blacklist); public BinaryResource(IAccessService accessService, ISearchService searchService, Provider<Role> roleProvider, @Named("auth.aas.blacklistedProvider") String blacklist) { super(searchService, roleProvider, blacklist); this.accessService = accessService; this.mimeUtil = new MimeUtil2(); this.mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.ExtensionMimeDetector"); this.mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector"); this.hf = Hashing.md5(); } /** * Reads a binary from a given item. * * @param httpHeaders The headers containing security information. * @param identifier The identifier of the AIP. * @param file The requested binary file. * @return An InputStream containing the binary data. * @throws WebApplicationException with the status of ith the status of 404 if the item or the component * do not exist. 500 if something went terribly wrong. */ @Path("{id}/{file: .+}") @GET @Produces("*/*") @UseAuthentication(profile = Profile.RESTRICTED_READ) public Response getBinary(@Context HttpHeaders httpHeaders, @PathParam("id") String identifier, @PathParam("file") String file, @Context HttpServletRequest request) { List<String> range = httpHeaders.getRequestHeader("Range"); List<String> ifRange = httpHeaders.getRequestHeader("If-Range"); LOG.debug("Accessing binary "); try { if (!securityCheck(identifier)) { return getResponse(); } Binary binary = this.accessService.getBinary(identifier, file); String mediaType = getMediaType(file); ResponseBuilder builder = createResponseBuilder(binary, range, file, ifRange); builder.type(mediaType); return builder.build(); } catch (ItemNotFoundException e) { LOG.info("binary for AIP: '{}/{}' not found.", identifier, file); return notFoundError(e, MediaType.APPLICATION_JSON_TYPE); } catch (Exception e) { LOG.error("binary for AIP: '" + identifier + "/" + file + "' not found.", e); return serverErrorError(MediaType.APPLICATION_JSON_TYPE, e); } } private String getMediaType(String fileName) { Collection<?> mimeTypes = this.mimeUtil.getMimeTypes(fileName); MimeType type = MimeUtil2.getMostSpecificMimeType(mimeTypes); return type.toString(); } private ResponseBuilder createResponseBuilder(final Binary binary, List<String> range, String file, List<String> ifRange) throws IOException { ByteRange byteRange; try { byteRange = ByteRange.parse(range, binary.getLength()); } catch (ParseException e) { return Response.status(416); } InputStream content = binary.getContent(); String actualETag = "\"" + this.hf.newHasher().putString(file).hash().toString() + "\""; if (byteRange == null) { return Response.ok(content).header("Accept-Ranges", "bytes").header("ETag", actualETag) .header("Expires", new Date(System.currentTimeMillis() + this.ONE_YEAR_IN_MS)) .header("Date", this.dateFormat.format(new Date())) .header("Content-Length", binary.getLength()); } String expectedETag = null; if ((ifRange != null) && !ifRange.isEmpty()) { expectedETag = ifRange.get(0); } if (expectedETag != null) { if (!actualETag.equals(expectedETag)) { return Response.status(Status.PRECONDITION_FAILED); } } if (!skipBytes(content, 0)) { return Response.status(416); } LimitInputStream entity = new LimitInputStream(content, binary.getLength()); return Response.status(206).entity(entity).header("Accept-Ranges", "bytes").header("ETag", actualETag) .header("Expires", new Date(System.currentTimeMillis() + this.ONE_YEAR_IN_MS)) .header("Date", this.dateFormat.format(new Date())).header("Content-Length", binary.getLength()) .header("Connection", "keep-alive").header("Keep-Alive", "timeout=100 max=1000") .header("Content-Range", "bytes " + 0 + "-" + (binary.getLength() - 1) + "/" + binary.getLength()); } private boolean skipBytes(InputStream binary, long count) throws IOException { long remaining = count; while (remaining > 0) { long skipped = binary.skip(remaining); if (skipped == 0) { return false; } remaining -= skipped; } return true; } }