package io.lumify.web.routes.vertex; import com.google.inject.Inject; import io.lumify.core.config.Configuration; import io.lumify.core.model.properties.LumifyProperties; import io.lumify.core.model.user.UserRepository; import io.lumify.core.model.workspace.WorkspaceRepository; import io.lumify.core.user.User; import io.lumify.miniweb.HandlerChain; import io.lumify.miniweb.utils.UrlUtils; import io.lumify.web.BaseRequestHandler; import org.apache.commons.io.IOUtils; import org.securegraph.Authorizations; import org.securegraph.Graph; import org.securegraph.Property; import org.securegraph.Vertex; import org.securegraph.property.StreamingPropertyValue; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.regex.Matcher; import java.util.regex.Pattern; public class VertexGetPropertyValue extends BaseRequestHandler { private static final Pattern RANGE_PATTERN = Pattern.compile("bytes=([0-9]*)-([0-9]*)"); private Graph graph; @Inject public VertexGetPropertyValue( final Graph graph, final UserRepository userRepository, final WorkspaceRepository workspaceRepository, final Configuration configuration) { super(userRepository, workspaceRepository, configuration); this.graph = graph; } @Override public void handle(HttpServletRequest request, HttpServletResponse response, HandlerChain chain) throws Exception { PlaybackOptions playbackOptions = new PlaybackOptions(); String graphVertexId = UrlUtils.urlDecode(getAttributeString(request, "graphVertexId")); String propertyName = UrlUtils.urlDecode(getAttributeString(request, "propertyName")); String propertyKey = UrlUtils.urlDecode(getAttributeString(request, "propertyKey")); playbackOptions.range = getOptionalParameter(request, "Range"); playbackOptions.download = getOptionalParameter(request, "download") != null; playbackOptions.playback = getOptionalParameter(request, "playback") != null; User user = getUser(request); Authorizations authorizations = getAuthorizations(request, user); handle(response, graphVertexId, propertyName, propertyKey, playbackOptions, authorizations); } public class PlaybackOptions { public String range; public boolean download; public boolean playback; } public void handle(HttpServletResponse response, String graphVertexId, String propertyName, String propertyKey, PlaybackOptions playbackOptions, Authorizations authorizations) throws IOException { Vertex vertex = graph.getVertex(graphVertexId, authorizations); if (vertex == null) { respondWithNotFound(response, String.format("vertex %s not found", graphVertexId)); return; } Property property = vertex.getProperty(propertyKey, propertyName); if (property == null) { respondWithNotFound(response, String.format("property %s:%s not found on vertex %s", propertyKey, propertyName, vertex.getId())); return; } handle(response, vertex, property, playbackOptions); } private void handle(HttpServletResponse response, Vertex vertex, Property property, PlaybackOptions playbackOptions) throws IOException { String fileName = getFileName(vertex); String mimeType = getMimeType(property); if (mimeType != null) { response.setContentType(mimeType); } setFileNameHeaders(response, fileName, playbackOptions); long totalLength; InputStream in; if (property.getValue() instanceof StreamingPropertyValue) { StreamingPropertyValue streamingPropertyValue = (StreamingPropertyValue) property.getValue(); in = streamingPropertyValue.getInputStream(); totalLength = streamingPropertyValue.getLength(); } else { byte[] value = property.getValue().toString().getBytes(); in = new ByteArrayInputStream(value); totalLength = value.length; } try { if (playbackOptions.playback) { handlePartialPlayback(response, in, totalLength, playbackOptions); } else { handleFullPlayback(response, in); } } finally { in.close(); } } private void setFileNameHeaders(HttpServletResponse response, String fileName, PlaybackOptions playbackOptions) { if (playbackOptions.download) { response.addHeader("Content-Disposition", "attachment; filename=" + fileName); } else { response.addHeader("Content-Disposition", "inline; filename=" + fileName); } } private String getFileName(Vertex vertex) { String fileName = LumifyProperties.FILE_NAME.getPropertyValue(vertex); if (fileName == null) { fileName = LumifyProperties.TITLE.getPropertyValue(vertex); } return fileName; } private void handleFullPlayback(HttpServletResponse response, InputStream in) throws IOException { IOUtils.copy(in, response.getOutputStream()); } private void handlePartialPlayback(HttpServletResponse response, InputStream in, long totalLength, PlaybackOptions playbackOptions) throws IOException { long partialStart = 0; Long partialEnd = null; if (playbackOptions.range != null) { response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); Matcher m = RANGE_PATTERN.matcher(playbackOptions.range); if (m.matches()) { partialStart = Long.parseLong(m.group(1)); if (m.group(2).length() > 0) { partialEnd = Long.parseLong(m.group(2)); } } } if (partialEnd == null) { partialEnd = totalLength; } // Ensure that the last byte position is less than the instance-length partialEnd = Math.min(partialEnd, totalLength - 1); long partialLength = totalLength; if (playbackOptions.range != null) { partialLength = partialEnd - partialStart + 1; response.addHeader("Content-Range", "bytes " + partialStart + "-" + partialEnd + "/" + totalLength); } response.addHeader("Content-Length", "" + partialLength); OutputStream out = response.getOutputStream(); IOUtils.copyLarge(in, out, partialStart, partialLength); response.flushBuffer(); } private String getMimeType(Property property) { String mimeType = LumifyProperties.MIME_TYPE.getMetadataValue(property.getMetadata(), null); if (mimeType != null) { return mimeType; } return null; } }