Skip to content

Commit

Permalink
LSNBLDR-720 resync LessonBuilderAccessService with kernel changes (sa…
Browse files Browse the repository at this point in the history
  • Loading branch information
ottenhoff authored Sep 6, 2016
1 parent a960895 commit 16707e5
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
package org.sakaiproject.content.api;

import java.io.InputStream;
import java.net.URI;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
Expand Down Expand Up @@ -2019,6 +2020,14 @@ public void restoreResource(String id) throws PermissionException, IdUsedExcepti
*/
public void removeDeletedResource(String resourceId) throws PermissionException, IdUnusedException, TypeException, InUseException;

/**
* Return a direct link to retrieve the asset instead of streaming the asset inside the JVM. See SAK-30325
*
* @param resourceId The file resource that may have a direct link available
*
* @return URI that will return the asset directly
*/
public URI getDirectLinkToAsset(ContentResource resource) throws Exception;

/**
* Expand the supplied resource under its parent collection. See KNL-273
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14311,7 +14311,14 @@ private String getMacroValue(String macroName) {
//unsupported, use macro name as is.
return macroName;
}


/*
* Return a direct link to the asset so we can bypass streaming the asset in the JVM
*/
public URI getDirectLinkToAsset(ContentResource resource) {
return m_storage.getDirectLink(resource);
}

/**
* Implementation of HardDeleteAware to allow content to be fully purged
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -620,26 +620,47 @@ public void handleAccess(HttpServletRequest req, HttpServletResponse res, Refere
// Wrap it in any filtering needed.
resource = contentFilterService.wrap(resource);

// from contenthosting
res.addHeader("Cache-Control", "must-revalidate, private");
res.addHeader("Expires", "-1");

// following cast is redundant is current kernels, but is needed for Sakai 2.6.1
long len = (long)resource.getContentLength();
String contentType = resource.getContentType();
ResourceProperties rp = resource.getProperties();
long lastModTime = 0;

try {
Time modTime = rp.getTimeProperty(ResourceProperties.PROP_MODIFIED_DATE);
lastModTime = modTime.getTime();
} catch (Exception e1) {
M_log.info("Could not retrieve modified time for: " + resource.getId());
}

// KNL-1316 tell the browser when our file was last modified for caching reasons
if (lastModTime > 0) {
SimpleDateFormat rfc1123Date = new SimpleDateFormat(RFC1123_DATE, LOCALE_US);
rfc1123Date.setTimeZone(TimeZone.getTimeZone("GMT"));
res.addHeader("Last-Modified", rfc1123Date.format(lastModTime));
}

// for url resource type, encode a redirect to the body URL
// in 2.10 have to check resourcetype, but in previous releasese
// it doesn't get copied in site copy, so check content type. 10 doesn't set the contenttype to url
// so we have to check both to work in all versions
if (contentType.equalsIgnoreCase(ResourceProperties.TYPE_URL) ||
"org.sakaiproject.content.types.urlResource".equalsIgnoreCase(resource.getResourceType())) {
"org.sakaiproject.content.types.urlResource".equalsIgnoreCase(resource.getResourceType()))
{
if (len < MAX_URL_LENGTH) {

byte[] content = resource.getContent();
if ((content == null) || (content.length == 0)) {
throw new IdUnusedException(ref.getReference());
}
// An invalid URI format will get caught by the
// outermost catch block
}

// An invalid URI format will get caught by the outermost catch block
URI uri = new URI(new String(content, "UTF-8"));
eventTrackingService.post(eventTrackingService.newEvent(ContentHostingService.EVENT_RESOURCE_READ,
resource.getReference(null), false));
eventTrackingService.post(eventTrackingService.newEvent(ContentHostingService.EVENT_RESOURCE_READ, resource.getReference(null), false));

String decodedUrl = null;
if (id.endsWith(".URL")) {
Expand All @@ -657,12 +678,12 @@ public void handleAccess(HttpServletRequest req, HttpServletResponse res, Refere
// long to issue as a redirect
throw new EntityNotDefinedException(ref.getReference());
}
} else {

// use the last part, the file name part of the id, for
// the download file name
}

else
{
// use the last part, the file name part of the id, for the download file name
String fileName = Web.encodeFileName(req, Validator.getFileName(ref.getId()));

String disposition = null;

boolean inline = false;
Expand All @@ -684,8 +705,6 @@ public void handleAccess(HttpServletRequest req, HttpServletResponse res, Refere
inline = true;
else {
// HTML and html is not allowed globally. code copied from BaseContentServices
ResourceProperties rp = resource.getProperties();

boolean fileInline = false;
boolean folderInline = false;

Expand Down Expand Up @@ -724,51 +743,62 @@ public void handleAccess(HttpServletRequest req, HttpServletResponse res, Refere
res.addHeader("X-Content-Security-Policy", "sandbox allow-forms allow-scripts allow-top-navigation allow-popups allow-pointer-lock");
}

// NOTE: Only set the encoding on the content we have
// to.
// Files uploaded by the user may have been created with
// different encodings, such as ISO-8859-1;
// rather than (sometimes wrongly) saying its UTF-8, let
// the browser auto-detect the encoding.
// If the content was created through the WYSIWYG
// editor, the encoding does need to be set (UTF-8).
// NOTE: Only set the encoding on the content we have to.
// Files uploaded by the user may have been created with different encodings, such as ISO-8859-1;
// rather than (sometimes wrongly) saying its UTF-8, let the browser auto-detect the encoding.
// If the content was created through the WYSIWYG editor, the encoding does need to be set (UTF-8).
String encoding = resource.getProperties().getProperty(ResourceProperties.PROP_CONTENT_ENCODING);
if (encoding != null && encoding.length() > 0) {
contentType = contentType + "; charset=" + encoding;
}

// from contenthosting
// KNL-1316 let's see if the user already has a cached copy. Code copied and modified from Tomcat DefaultServlet.java
long headerValue = req.getDateHeader("If-Modified-Since");
if (headerValue != -1 && (lastModTime < headerValue + 1000)) {
// The entity has not been modified since the date specified by the client. This is not an error case.
res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}

res.addHeader("Cache-Control", "must-revalidate, private");
res.addHeader("Expires", "-1");
ResourceProperties rp = resource.getProperties();
long lastModTime = 0;
// If there is a direct link to the asset, no sense streaming it.
// Send the asset directly to the load-balancer or to the client
URI directLinkUri = contentHostingService.getDirectLinkToAsset(resource);

try {
Time modTime = rp.getTimeProperty(ResourceProperties.PROP_MODIFIED_DATE);
lastModTime = modTime.getTime();
} catch (Exception e1) {
M_log.info("Could not retrieve modified time for: " + resource.getId());
}

// KNL-1316 tell the browser when our file was last modified for caching reasons
if (lastModTime > 0) {
SimpleDateFormat rfc1123Date = new SimpleDateFormat(RFC1123_DATE, LOCALE_US);
rfc1123Date.setTimeZone(TimeZone.getTimeZone("GMT"));
res.addHeader("Last-Modified", rfc1123Date.format(lastModTime));
}
ArrayList<Range> ranges = parseRange(req, res, len);

// KNL-1316 let's see if the user already has a cached copy. Code copied and modified from Tomcat DefaultServlet.java
long headerValue = req.getDateHeader("If-Modified-Since");
if (headerValue != -1 && (lastModTime < headerValue + 1000)) {
// The entity has not been modified since the date specified by the client. This is not an error case.
res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
if (directLinkUri != null || req.getHeader("Range") == null || (ranges == null) || (ranges.isEmpty())) {
res.addHeader("Accept-Ranges", "none");
res.setContentType(contentType);
res.addHeader("Content-Disposition", disposition);
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4187336
if (len <= Integer.MAX_VALUE) {
res.setContentLength((int)len);
} else {
res.addHeader("Content-Length", Long.toString(len));
}

ArrayList<Range> ranges = parseRange(req, res, len);
// SAK-30455: Track event now so the direct link still records a content.read
eventTrackingService.post(eventTrackingService.newEvent(ContentHostingService.EVENT_RESOURCE_READ, resource.getReference(null), false));

if (req.getHeader("Range") == null || (ranges == null) || (ranges.isEmpty())) {
// Bypass loading the asset and just send the user a link to it.
if (directLinkUri != null) {
if (ServerConfigurationService.getBoolean("cloud.content.sendfile", false)) {
int hostLength = new String(directLinkUri.getScheme() + "://" + directLinkUri.getHost()).length();
String linkPath = "/sendfile" + directLinkUri.toString().substring(hostLength);
if (M_log.isDebugEnabled()) {
M_log.debug("X-Sendfile: " + linkPath);
}

// Nginx uses X-Accel-Redirect and Apache and others use X-Sendfile
res.addHeader("X-Accel-Redirect", linkPath);
res.addHeader("X-Sendfile", linkPath);
return;
}
else if (ServerConfigurationService.getBoolean("cloud.content.directurl", true)) {
res.sendRedirect(directLinkUri.toString());
return;
}
}

// stream the content using a small buffer to keep memory managed
InputStream content = null;
Expand All @@ -782,16 +812,6 @@ public void handleAccess(HttpServletRequest req, HttpServletResponse res, Refere
throw new IdUnusedException(ref.getReference());
}

res.setContentType(contentType);
res.addHeader("Content-Disposition", disposition);
res.addHeader("Accept-Ranges", "bytes");

// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4187336
if (len <= Integer.MAX_VALUE){
res.setContentLength((int)len);
} else {
res.addHeader("Content-Length", Long.toString(len));
}

// set the buffer of the response to match what we are reading from the request
if (len < STREAM_BUFFER_SIZE)
Expand Down Expand Up @@ -833,15 +853,12 @@ public void handleAccess(HttpServletRequest req, HttpServletResponse res, Refere
}
}
}

// Track event - only for full reads
eventTrackingService.post(eventTrackingService.newEvent(ContentHostingService.EVENT_RESOURCE_READ, resource.getReference(null), false));

}
else
{
// Output partial content. Adapted from Apache Tomcat 5.5.27 DefaultServlet.java
res.addHeader("Accept-Ranges", "bytes");
res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

if (ranges.size() == 1) {
Expand Down

0 comments on commit 16707e5

Please sign in to comment.