WICKET-5819 Support for HTML 5 media tags (audio / video) Move range handling code to AbstractResource. Remove MediaStreamingResourceReference.
Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/f623ceaa Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/f623ceaa Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/f623ceaa Branch: refs/heads/master Commit: f623ceaa93696427aafa8a15ecc86f2d0e362969 Parents: 499f11f Author: Martin Tzvetanov Grigorov <mgrigo...@apache.org> Authored: Wed Mar 25 23:29:51 2015 +0200 Committer: Martin Tzvetanov Grigorov <mgrigo...@apache.org> Committed: Mon Mar 30 21:59:14 2015 +0300 ---------------------------------------------------------------------- .../markup/html/media/MediaComponent.java | 29 +- .../media/MediaStreamingResourceReference.java | 182 ------------ .../markup/html/media/PartWriterCallback.java | 185 ------------ .../apache/wicket/markup/html/media/Source.java | 36 ++- .../wicket/markup/html/media/audio/Audio.java | 18 +- .../wicket/markup/html/media/video/Video.java | 18 +- .../request/resource/AbstractResource.java | 280 +++++++++++++++++-- .../request/resource/PackageResource.java | 90 +++--- .../request/resource/PartWriterCallback.java | 184 ++++++++++++ .../html/media/MediaTagsExtendedTestPage.java | 9 +- .../markup/html/media/MediaTagsTestPage.java | 8 +- .../org/apache/wicket/examples/media/Home.java | 7 +- .../apache/wicket/request/http/WebResponse.java | 52 +++- 13 files changed, 583 insertions(+), 515 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java index 093cd8e..83186f9 100755 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaComponent.java @@ -23,6 +23,7 @@ import org.apache.wicket.request.Url; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.mapper.parameter.PageParametersEncoder; +import org.apache.wicket.request.resource.PackageResourceReference; /** * The media component is used to provide basic functionality to the video and audio component. The @@ -100,7 +101,7 @@ public abstract class MediaComponent extends WebMarkupContainer private final PageParameters pageParameters; - private final MediaStreamingResourceReference mediaStreamingResourceReference; + private final PackageResourceReference resourceReference; private final String url; @@ -133,31 +134,31 @@ public abstract class MediaComponent extends WebMarkupContainer * * @param id * The component id - * @param mediaStreamingResourceReference + * @param resourceReference */ - public MediaComponent(String id, MediaStreamingResourceReference mediaStreamingResourceReference) + public MediaComponent(String id, PackageResourceReference resourceReference) { - this(id, null, null, null, mediaStreamingResourceReference); + this(id, null, null, null, resourceReference); } public MediaComponent(String id, IModel<?> model, - MediaStreamingResourceReference mediaStreamingResourceReference) + PackageResourceReference resourceReference) { - this(id, model, null, null, mediaStreamingResourceReference); + this(id, model, null, null, resourceReference); } public MediaComponent(String id, - MediaStreamingResourceReference mediaStreamingResourceReference, + PackageResourceReference resourceReference, PageParameters pageParameters) { - this(id, null, null, pageParameters, mediaStreamingResourceReference); + this(id, null, null, pageParameters, resourceReference); } public MediaComponent(String id, IModel<?> model, - MediaStreamingResourceReference mediaStreamingResourceReference, + PackageResourceReference resourceReference, PageParameters pageParameters) { - this(id, model, null, pageParameters, mediaStreamingResourceReference); + this(id, model, null, pageParameters, resourceReference); } public MediaComponent(String id, String url) @@ -176,12 +177,12 @@ public abstract class MediaComponent extends WebMarkupContainer } private MediaComponent(String id, IModel<?> model, String url, PageParameters pageParameters, - MediaStreamingResourceReference mediaStreamingResourceReference) + PackageResourceReference resourceReference) { super(id, model); this.url = url; this.pageParameters = pageParameters; - this.mediaStreamingResourceReference = mediaStreamingResourceReference; + this.resourceReference = resourceReference; } @Override @@ -197,10 +198,10 @@ public abstract class MediaComponent extends WebMarkupContainer timeManagement += "#t=" + startTime + (endTime != null ? "," + endTime : ""); } - if (mediaStreamingResourceReference != null) + if (resourceReference != null) { CharSequence urlToMediaReference = RequestCycle.get().urlFor( - mediaStreamingResourceReference, pageParameters); + resourceReference, pageParameters); tag.put("src", urlToMediaReference + timeManagement); } else if (url != null) http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaStreamingResourceReference.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaStreamingResourceReference.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaStreamingResourceReference.java deleted file mode 100755 index 76a208f..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/MediaStreamingResourceReference.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ -package org.apache.wicket.markup.html.media; - -import java.io.IOException; -import java.util.Locale; - -import org.apache.wicket.Application; -import org.apache.wicket.WicketRuntimeException; -import org.apache.wicket.request.Request; -import org.apache.wicket.request.Response; -import org.apache.wicket.request.http.WebRequest; -import org.apache.wicket.request.http.WebResponse; -import org.apache.wicket.request.resource.ContentDisposition; -import org.apache.wicket.request.resource.PackageResource; -import org.apache.wicket.request.resource.PackageResourceReference; -import org.apache.wicket.util.resource.IResourceStream; -import org.apache.wicket.util.string.Strings; - -/** - * The media streaming resource reference is used to provided streamed data based on bytes requested - * by the client for video and audio files - * - * @author Tobias Soloschenko - */ -public class MediaStreamingResourceReference extends PackageResourceReference -{ - private static final long serialVersionUID = 1L; - - public MediaStreamingResourceReference(Class<?> scope, String name, Locale locale, - String style, String variation) - { - super(scope, name, locale, style, variation); - } - - public MediaStreamingResourceReference(Class<?> scope, String name) - { - this(scope, name, null, null, null); - } - - public MediaStreamingResourceReference(Key key) - { - super(key); - } - - public MediaStreamingResourceReference(String name) - { - super(name); - } - - @Override - public PackageResource getResource() - { - return new PackageResource(getScope(), getName(), getLocale(), getStyle(), getVariation()) - { - private static final long serialVersionUID = 1L; - - @Override - protected ResourceResponse newResourceResponse(Attributes attributes) - { - IResourceStream resourceStream = getResourceStream(); - if (resourceStream == null) - { - throw new WicketRuntimeException("Cannot find resource: " + toString()); - } - try - { - Request request = attributes.getRequest(); - Response response = attributes.getResponse(); - - if (!(request instanceof WebRequest) || !(response instanceof WebResponse)) - { - throw new IllegalStateException( - "Web request/response are required! Request: " + request + - ", response: " + response); - } - - WebRequest webRequest = (WebRequest)request; - WebResponse webResponse = (WebResponse)response; - - long length = resourceStream.length().bytes(); - - ResourceResponse resourceResponse = new ResourceResponse(); - resourceResponse.setContentType(resourceStream.getContentType()); - resourceResponse.setFileName(MediaStreamingResourceReference.this.getName()); - resourceResponse.setContentDisposition(ContentDisposition.ATTACHMENT); - resourceResponse.setLastModified(resourceStream.lastModifiedTime()); - - // accept ranges, so that the player can - // load and play content from a specific byte position - webResponse.setHeader("Accept-Range", "bytes"); - - Long startbyte = null; - Long endbyte = null; - - // Calculating the response code and the byte range to be played - String rangeHeader = webRequest.getHeader("range"); - if (Strings.isEmpty(rangeHeader)) - { - resourceResponse.setStatusCode(200); - resourceResponse.setContentLength(length); - } - else - { - rangeHeader = rangeHeader.replaceAll(" ", ""); - // partial content has to be returned - resourceResponse.setStatusCode(206); - - // And now the calculation of the range to be read - // and to be given as response within the Content-Range header - // for more information take a look here: - // http://stackoverflow.com/questions/8293687/sample-http-range-request-session - String range = rangeHeader.substring(rangeHeader.indexOf('=') + 1, - rangeHeader.length()); - String[] rangeParts = Strings.split(range, '-'); - - String startByteString = rangeParts[0]; - String endByteString = rangeParts[1]; - - startbyte = startByteString != null && !startByteString.trim().equals("") - ? Long.parseLong(startByteString) : 0; - endbyte = endByteString != null && !endByteString.trim().equals("") - ? Long.parseLong(endByteString) : length - 1; - - webResponse.setHeader("Content-Range", "bytes " + startbyte + '-' + - endbyte + '/' + length); - resourceResponse.setContentLength((endbyte - startbyte) + 1); - } - - // Apply the writer callback to send the requested part to the client - resourceResponse.setWriteCallback(new PartWriterCallback(resourceStream, - startbyte, endbyte)); - - return resourceResponse; - } - catch (Exception e) - { - throw new WicketRuntimeException( - "A problem occurred while creating the video response.", e); - } - finally - { - try - { - resourceStream.close(); - } - catch (IOException e) - { - throw new WicketRuntimeException( - "A problem occurred while closing the video response stream.", e); - } - } - } - }; - - } - - /** - * Returns the mime type of the media this resource reference belongs to - * - * @return the mime type of this media - */ - public String getType() - { - final String resourceName = MediaStreamingResourceReference.this.getName(); - return Application.get().getMimeType(resourceName); - } -} http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/PartWriterCallback.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/PartWriterCallback.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/PartWriterCallback.java deleted file mode 100644 index da8abda..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/PartWriterCallback.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ -package org.apache.wicket.markup.html.media; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import org.apache.wicket.WicketRuntimeException; -import org.apache.wicket.protocol.http.servlet.ResponseIOException; -import org.apache.wicket.request.resource.AbstractResource.WriteCallback; -import org.apache.wicket.request.resource.IResource.Attributes; -import org.apache.wicket.util.io.Streams; -import org.apache.wicket.util.resource.IResourceStream; - -/** - * Used to read a part of the package resource stream and write it to the output stream of the - * response. - * - * @author Tobias Soloschenko - * - */ -public class PartWriterCallback extends WriteCallback -{ - private final IResourceStream resourceStream; - - private final Long startbyte; - - private Long endbyte; - - private int bufferSize; - - /** - * Creates a part writer callback.<br> - * <br> - * Reads a part of the given resource stream. If the startbyte parameter is not null the - * number of bytes are skipped till the stream is read. If the endbyte is not null the stream is - * read till endbyte, else to the end of the whole stream. If startbyte and endbyte is null the - * whole stream is read. - * - * @param resourceStream - * the resource stream to read - * @param startbyte - * the start position to read from (if not null the number of bytes are skipped till - * the stream is read) - * @param endbyte - * the end position to read to (if not null the stream is going to be read till - * endbyte, else to the end of the whole stream) - */ - public PartWriterCallback(IResourceStream resourceStream, Long startbyte, - Long endbyte) - { - this.resourceStream = resourceStream; - this.startbyte = startbyte; - this.endbyte = endbyte; - } - - /** - * Writes the data - * - * @param attributes - * the attributes to get the output stream of the response - */ - @Override - public void writeData(Attributes attributes) throws IOException - { - try - { - InputStream inputStream = resourceStream.getInputStream(); - OutputStream outputStream = attributes.getResponse().getOutputStream(); - byte[] buffer = new byte[getBufferSize()]; - - if (startbyte != null || endbyte != null) - { - // skipping the first bytes which are - // requested to be skipped by the client - if (startbyte != null) - { - inputStream.skip(startbyte); - } - - // If there are no end bytes given read the whole stream till the end - if (endbyte == null) - { - endbyte = resourceStream.length().bytes(); - } - - long totalBytes = 0; - int actualReadBytes; - - while ((actualReadBytes = inputStream.read(buffer)) != -1) - { - totalBytes = totalBytes + buffer.length; - long lowerBuffer = endbyte - totalBytes; - if (lowerBuffer <= 0) - { - buffer = resizeArray(buffer, actualReadBytes); - outputStream.write(buffer); - break; - } - else - { - outputStream.write(buffer); - } - } - } - else - { - Streams.copy(inputStream, outputStream, getBufferSize()); - } - } - catch (ResponseIOException e) - { - // the client has closed the connection and - // doesn't read the stream further on - // (in tomcats - // org.apache.catalina.connector.ClientAbortException) - // we ignore this case - } - catch (Exception e) - { - throw new WicketRuntimeException( - "A problem occurred while writing the buffer to the output stream.", e); - } - } - - /** - * Reallocates an array with a new size, and copies the contents of the old array to the new - * array. - * - * @param oldArray - * the old array, to be reallocated. - * @param newSize - * the new array size. - * @return A new array with the same contents. - */ - @SuppressWarnings("rawtypes") - private static byte[] resizeArray(byte[] oldArray, int newSize) - { - int oldSize = oldArray.length; - byte[] newArray = new byte[newSize]; - int minLength = Math.min(oldSize, newSize); - if (minLength > 0) - { - System.arraycopy(oldArray, 0, newArray, 0, minLength); - } - return newArray; - } - - /** - * Sets the buffer size used to send the data to the client - * - * @return the buffer size used to send the data to the client (default is 4096) - */ - public int getBufferSize() - { - return bufferSize > 0 ? bufferSize : 4096; - } - - /** - * Sets the buffer size used to send the data to the client - * - * @param bufferSize - * the buffer size used to send the data to the client - */ - public void setBufferSize(int bufferSize) - { - this.bufferSize = bufferSize; - } - -} http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java index 76b4002..6b19e60 100755 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/Source.java @@ -21,6 +21,9 @@ import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.model.IModel; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.apache.wicket.request.resource.PackageResource; +import org.apache.wicket.request.resource.PackageResourceReference; +import org.apache.wicket.util.resource.IResourceStream; /** * The source of an audio or a video media component @@ -38,7 +41,7 @@ public class Source extends WebMarkupContainer private String media; - private final MediaStreamingResourceReference mediaStreamingResourceReference; + private final PackageResourceReference resourceReference; private final PageParameters pageParameters; @@ -54,28 +57,28 @@ public class Source extends WebMarkupContainer this(id, model, null, null, null); } - public Source(String id, MediaStreamingResourceReference mediaStreamingResourceReference) + public Source(String id, PackageResourceReference resourceReference) { - this(id, null, null, null, mediaStreamingResourceReference); + this(id, null, null, null, resourceReference); } public Source(String id, IModel<?> model, - MediaStreamingResourceReference mediaStreamingResourceReference) + PackageResourceReference resourceReference) { - this(id, model, null, null, mediaStreamingResourceReference); + this(id, model, null, null, resourceReference); } - public Source(String id, MediaStreamingResourceReference mediaStreamingResourceReference, + public Source(String id, PackageResourceReference resourceReference, PageParameters pageParameters) { - this(id, null, null, pageParameters, mediaStreamingResourceReference); + this(id, null, null, pageParameters, resourceReference); } public Source(String id, IModel<?> model, - MediaStreamingResourceReference mediaStreamingResourceReference, + PackageResourceReference resourceReference, PageParameters pageParameters) { - this(id, model, null, pageParameters, mediaStreamingResourceReference); + this(id, model, null, pageParameters, resourceReference); } public Source(String id, String url) @@ -89,12 +92,12 @@ public class Source extends WebMarkupContainer } private Source(String id, IModel<?> model, String url, PageParameters pageParameters, - MediaStreamingResourceReference mediaStreamingResourceReference) + PackageResourceReference resourceReference) { super(id, model); this.url = url; this.pageParameters = pageParameters; - this.mediaStreamingResourceReference = mediaStreamingResourceReference; + this.resourceReference = resourceReference; } @Override @@ -103,9 +106,9 @@ public class Source extends WebMarkupContainer checkComponentTag(tag, "source"); super.onComponentTag(tag); - if (mediaStreamingResourceReference != null) + if (resourceReference != null) { - CharSequence url = RequestCycle.get().urlFor(mediaStreamingResourceReference, pageParameters); + CharSequence url = RequestCycle.get().urlFor(resourceReference, pageParameters); tag.put("src", url); } else if (url != null) { @@ -118,9 +121,12 @@ public class Source extends WebMarkupContainer { tag.put("type", type); } - else if (mediaStreamingResourceReference != null) + else if (resourceReference != null) { - tag.put("type", mediaStreamingResourceReference.getType()); + PackageResource resource = resourceReference.getResource(); + IResourceStream resourceStream = resource.getResourceStream(); + String contentType = resourceStream.getContentType(); + tag.put("type", contentType); } } http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java index e16cb8b..c0592fa 100755 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/audio/Audio.java @@ -18,9 +18,9 @@ package org.apache.wicket.markup.html.media.audio; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.html.media.MediaComponent; -import org.apache.wicket.markup.html.media.MediaStreamingResourceReference; import org.apache.wicket.model.IModel; import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.apache.wicket.request.resource.PackageResourceReference; /** * An audio media component to playback audio files. @@ -42,28 +42,28 @@ public class Audio extends MediaComponent super(id, model); } - public Audio(String id, MediaStreamingResourceReference mediaStreamingResourceReference) + public Audio(String id, PackageResourceReference resourceReference) { - super(id, mediaStreamingResourceReference); + super(id, resourceReference); } public Audio(String id, IModel<?> model, - MediaStreamingResourceReference mediaStreamingResourceReference) + PackageResourceReference resourceReference) { - super(id, model, mediaStreamingResourceReference); + super(id, model, resourceReference); } - public Audio(String id, MediaStreamingResourceReference mediaStreamingResourceReference, + public Audio(String id, PackageResourceReference resourceReference, PageParameters pageParameters) { - super(id, mediaStreamingResourceReference, pageParameters); + super(id, resourceReference, pageParameters); } public Audio(String id, IModel<?> model, - MediaStreamingResourceReference mediaStreamingResourceReference, + PackageResourceReference resourceReference, PageParameters pageParameters) { - super(id, model, mediaStreamingResourceReference, pageParameters); + super(id, model, resourceReference, pageParameters); } public Audio(String id, String url) http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java index b73ba3d..1106696 100755 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/media/video/Video.java @@ -18,10 +18,10 @@ package org.apache.wicket.markup.html.media.video; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.html.media.MediaComponent; -import org.apache.wicket.markup.html.media.MediaStreamingResourceReference; import org.apache.wicket.model.IModel; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.apache.wicket.request.resource.PackageResourceReference; import org.apache.wicket.request.resource.ResourceReference; /** @@ -52,28 +52,28 @@ public class Video extends MediaComponent super(id, model); } - public Video(String id, MediaStreamingResourceReference mediaStreamingResourceReference) + public Video(String id, PackageResourceReference resourceReference) { - super(id, mediaStreamingResourceReference); + super(id, resourceReference); } public Video(String id, IModel<?> model, - MediaStreamingResourceReference mediaStreamingResourceReference) + PackageResourceReference resourceReference) { - super(id, model, mediaStreamingResourceReference); + super(id, model, resourceReference); } - public Video(String id, MediaStreamingResourceReference mediaStreamingResourceReference, + public Video(String id, PackageResourceReference resourceReference, PageParameters pageParameters) { - super(id, mediaStreamingResourceReference, pageParameters); + super(id, resourceReference, pageParameters); } public Video(String id, IModel<?> model, - MediaStreamingResourceReference mediaStreamingResourceReference, + PackageResourceReference resourceReference, PageParameters pageParameters) { - super(id, model, mediaStreamingResourceReference, pageParameters); + super(id, model, resourceReference, pageParameters); } public Video(String id, String url) http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java index 386b7b3..18764d4 100644 --- a/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java +++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/AbstractResource.java @@ -24,9 +24,12 @@ import java.util.Set; import javax.servlet.http.HttpServletResponse; import org.apache.wicket.Application; +import org.apache.wicket.MetaDataKey; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.request.HttpHeaderCollection; +import org.apache.wicket.request.Request; import org.apache.wicket.request.Response; +import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.http.WebRequest; import org.apache.wicket.request.http.WebResponse; import org.apache.wicket.request.resource.caching.IResourceCachingStrategy; @@ -34,6 +37,7 @@ import org.apache.wicket.request.resource.caching.IStaticCacheableResource; import org.apache.wicket.util.io.Streams; import org.apache.wicket.util.lang.Args; import org.apache.wicket.util.lang.Classes; +import org.apache.wicket.util.string.Strings; import org.apache.wicket.util.time.Duration; import org.apache.wicket.util.time.Time; @@ -50,6 +54,39 @@ public abstract class AbstractResource implements IResource /** header values that are managed internally and must not be set directly */ public static final Set<String> INTERNAL_HEADERS; + /** The meta data key of the content range start byte **/ + public static final MetaDataKey<Long> CONTENT_RANGE_STARTBYTE = new MetaDataKey<Long>() + { + private static final long serialVersionUID = 1L; + }; + + /** The meta data key of the content range end byte **/ + public static final MetaDataKey<Long> CONTENT_RANGE_ENDBYTE = new MetaDataKey<Long>() + { + private static final long serialVersionUID = 1L; + }; + + /** + * All available content range types. The type name represents the name used in header + * information. + */ + public enum ContentRangeType + { + BYTES("bytes"), NONE("none"); + + private final String typeName; + + private ContentRangeType(String typeName) + { + this.typeName = typeName; + } + + public String getTypeName() + { + return typeName; + } + } + static { INTERNAL_HEADERS = new HashSet<>(); @@ -63,6 +100,8 @@ public abstract class AbstractResource implements IResource INTERNAL_HEADERS.add("transfer-encoding"); INTERNAL_HEADERS.add("connection"); INTERNAL_HEADERS.add("content-disposition"); + INTERNAL_HEADERS.add("content-range"); + INTERNAL_HEADERS.add("accept-range"); } /** @@ -94,6 +133,8 @@ public abstract class AbstractResource implements IResource private String fileName = null; private ContentDisposition contentDisposition = ContentDisposition.INLINE; private String contentType = null; + private String contentRange = null; + private ContentRangeType contentRangeType = null; private String textEncoding; private long contentLength = -1; private Time lastModified = null; @@ -262,8 +303,53 @@ public abstract class AbstractResource implements IResource } /** - * Sets the text encoding for the resource. This setting must only used - * if the resource response represents text. + * Gets the content range of the resource. If no content range is set the client assumes the + * whole content. + * + * @return the content range + */ + public String getContentRange() + { + return contentRange; + } + + /** + * Sets the content range of the resource. If no content range is set the client assumes the + * whole content. Please note that if the content range is set, the content length, the + * status code and the accept range must be set right, too. + * + * @param contentRange + * the content range + */ + public void setContentRange(String contentRange) + { + this.contentRange = contentRange; + } + + /** + * If the resource accepts ranges + * + * @return the type of range (e.g. bytes) + */ + public ContentRangeType getAcceptRange() + { + return contentRangeType; + } + + /** + * Sets the accept range header (e.g. bytes) + * + * @param contentRangeType + * the content range header information + */ + public void setAcceptRange(ContentRangeType contentRangeType) + { + this.contentRangeType = contentRangeType; + } + + /** + * Sets the text encoding for the resource. This setting must only used if the resource + * response represents text. * * @param textEncoding * character encoding of text body @@ -534,6 +620,9 @@ public abstract class AbstractResource implements IResource @Override public void respond(final Attributes attributes) { + // Sets the request attributes + setRequestMetaData(attributes); + // Get a "new" ResourceResponse to write a response ResourceResponse data = newResourceResponse(attributes); @@ -552,8 +641,8 @@ public abstract class AbstractResource implements IResource // set response header setResponseHeaders(data, attributes); - if (!data.dataNeedsToBeWritten(attributes) || data.getErrorCode() != null - || needsBody(data.getStatusCode()) == false) + if (!data.dataNeedsToBeWritten(attributes) || data.getErrorCode() != null || + needsBody(data.getStatusCode()) == false) { return; } @@ -574,19 +663,19 @@ public abstract class AbstractResource implements IResource } /** - * Decides whether a response body should be written back to the client depending - * on the set status code + * Decides whether a response body should be written back to the client depending on the set + * status code * * @param statusCode - * the status code set by the application + * the status code set by the application * @return {@code true} if the status code allows response body, {@code false} - otherwise */ private boolean needsBody(Integer statusCode) { return statusCode == null || - (statusCode < 300 && - statusCode != HttpServletResponse.SC_NO_CONTENT && - statusCode != HttpServletResponse.SC_RESET_CONTENT); + (statusCode < 300 && + statusCode != HttpServletResponse.SC_NO_CONTENT && + statusCode != HttpServletResponse.SC_RESET_CONTENT); } /** @@ -606,15 +695,69 @@ public abstract class AbstractResource implements IResource { throw new IllegalArgumentException("you are not allowed to directly access header [" + name + "], " + "use one of the other specialized methods of " + - Classes.simpleName(getClass()) + " to get or modify its value"); + Classes.simpleName(getClass()) + " to get or modify its value"); } } /** - * @param data + * Reads the plain request header information and applies enriched information as meta data to + * the current request. Those information are available for the whole request cycle. + * + * @param attributes + * the attributes to get the plain request header information + */ + protected void setRequestMetaData(Attributes attributes) + { + Request request = attributes.getRequest(); + if (request instanceof WebRequest) + { + WebRequest webRequest = (WebRequest)request; + + setRequestRangeMetaData(webRequest); + } + } + + protected void setRequestRangeMetaData(WebRequest webRequest) + { + String rangeHeader = webRequest.getHeader("range"); + + // The content range header is only be calculated if a range is given + if (!Strings.isEmpty(rangeHeader) && + rangeHeader.contains(ContentRangeType.BYTES.getTypeName())) + { + // fixing white spaces + rangeHeader = rangeHeader.replaceAll(" ", ""); + + String range = rangeHeader.substring(rangeHeader.indexOf('=') + 1, + rangeHeader.length()); + + String[] rangeParts = Strings.split(range, '-'); + + String startByteString = rangeParts[0]; + String endByteString = rangeParts[1]; + + Long startbyte = startByteString != null && !startByteString.trim().equals("") + ? Long.parseLong(startByteString) : 0; + Long endbyte = endByteString != null && !endByteString.trim().equals("") + ? Long.parseLong(endByteString) : -1; + + // Make the content range information available for the whole request cycle + RequestCycle.get().setMetaData(CONTENT_RANGE_STARTBYTE, startbyte); + RequestCycle.get().setMetaData(CONTENT_RANGE_ENDBYTE, endbyte); + } + } + + /** + * Sets the response header of resource response to the response received from the attributes + * + * @param resourceResponse + * the resource response to get the header fields from * @param attributes + * the attributes to get the response from to which the header information are going + * to be applied */ - protected void setResponseHeaders(final ResourceResponse data, final Attributes attributes) + protected void setResponseHeaders(final ResourceResponse resourceResponse, + final Attributes attributes) { Response response = attributes.getResponse(); if (response instanceof WebResponse) @@ -622,38 +765,36 @@ public abstract class AbstractResource implements IResource WebResponse webResponse = (WebResponse)response; // 1. Last Modified - Time lastModified = data.getLastModified(); + Time lastModified = resourceResponse.getLastModified(); if (lastModified != null) { webResponse.setLastModifiedTime(lastModified); } // 2. Caching - configureCache(data, attributes); + configureCache(resourceResponse, attributes); - if (data.getErrorCode() != null) + if (resourceResponse.getErrorCode() != null) { - webResponse.sendError(data.getErrorCode(), data.getErrorMessage()); + webResponse.sendError(resourceResponse.getErrorCode(), + resourceResponse.getErrorMessage()); return; } - if (data.getStatusCode() != null) + if (resourceResponse.getStatusCode() != null) { - webResponse.setStatus(data.getStatusCode()); + webResponse.setStatus(resourceResponse.getStatusCode()); } - - if (!data.dataNeedsToBeWritten(attributes)) + + if (!resourceResponse.dataNeedsToBeWritten(attributes)) { webResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return; } - String fileName = data.getFileName(); - ContentDisposition disposition = data.getContentDisposition(); - String mimeType = data.getContentType(); - long contentLength = data.getContentLength(); - // 3. Content Disposition + String fileName = resourceResponse.getFileName(); + ContentDisposition disposition = resourceResponse.getContentDisposition(); if (ContentDisposition.ATTACHMENT == disposition) { webResponse.setAttachmentHeader(fileName); @@ -664,10 +805,11 @@ public abstract class AbstractResource implements IResource } // 4. Mime Type (+ encoding) + String mimeType = resourceResponse.getContentType(); if (mimeType != null) { - final String encoding = data.getTextEncoding(); - + final String encoding = resourceResponse.getTextEncoding(); + if (encoding == null) { webResponse.setContentType(mimeType); @@ -678,14 +820,46 @@ public abstract class AbstractResource implements IResource } } - // 5. Content Length - if (contentLength != -1) + // 5. Accept Range + ContentRangeType acceptRange = resourceResponse.getAcceptRange(); + if (acceptRange != null) + { + webResponse.setAcceptRange(acceptRange.getTypeName()); + } + + long contentLength = resourceResponse.getContentLength(); + boolean contentRangeApplied = false; + + // 6. Content Range + // for more information take a look here: + // http://stackoverflow.com/questions/8293687/sample-http-range-request-session + // if the content range header has been set directly + // to the resource response use it otherwise calculate it + String contentRange = resourceResponse.getContentRange(); + if (contentRange != null) + { + webResponse.setContentRange(contentRange); + } + else + { + // content length has to be set otherwise the content range header can not be + // calculated - accept range must be set to bytes - others are not supported at the + // moment + if (contentLength != -1 && ContentRangeType.BYTES.equals(acceptRange)) + { + contentRangeApplied = setResponseContentRangeHeaderFields(webResponse, + attributes, contentLength); + } + } + + // 7. Content Length + if (contentLength != -1 && !contentRangeApplied) { webResponse.setContentLength(contentLength); } // add custom headers and values - final HttpHeaderCollection headers = data.getHeaders(); + final HttpHeaderCollection headers = resourceResponse.getHeaders(); for (String name : headers.getHeaderNames()) { @@ -698,6 +872,50 @@ public abstract class AbstractResource implements IResource } } } + + /** + * Sets the content range header fields to the given web response + * + * @param webResponse + * the web response to apply the content range information to + * @param attributes + * the attributes to get the request from + * @param contentLength + * the content length of the response + * @return if the content range header information has been applied + */ + protected boolean setResponseContentRangeHeaderFields(WebResponse webResponse, + Attributes attributes, long contentLength) + { + boolean contentRangeApplied = false; + if (attributes.getRequest() instanceof WebRequest) + { + Long startbyte = RequestCycle.get().getMetaData(CONTENT_RANGE_STARTBYTE); + Long endbyte = RequestCycle.get().getMetaData(CONTENT_RANGE_ENDBYTE); + + if (startbyte != null && endbyte != null) + { + // if end byte hasn't been set + if (endbyte == -1) + { + endbyte = contentLength - 1; + } + + // Change the status code to 206 partial content + webResponse.setStatus(206); + // currently only bytes are supported. + webResponse.setContentRange(ContentRangeType.BYTES.getTypeName() + " " + startbyte + + '-' + endbyte + '/' + contentLength); + // content length must be overridden by the recalculated one + webResponse.setContentLength((endbyte - startbyte) + 1); + + // content range has been applied do not set the content length again! + contentRangeApplied = true; + } + } + return contentRangeApplied; + } + /** * Callback invoked when resource data needs to be written to response. Subclass needs to * implement the {@link #writeData(org.apache.wicket.request.resource.IResource.Attributes)} http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java index 8c5b457..ab66014 100644 --- a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java +++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java @@ -136,15 +136,13 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl * controls whether {@link org.apache.wicket.request.resource.caching.IResourceCachingStrategy} * should be applied to resource */ - private boolean cachingEnabled = true; - + /** * text encoding (may be null) - only makes sense for character-based resources */ - private String textEncoding = null; - + /** * Hidden constructor. * @@ -189,7 +187,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl { return Session.get().getLocale(); } - + return locale; } @@ -199,7 +197,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl { return Session.get().getStyle(); } - + return style; } @@ -216,7 +214,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl /** * get text encoding (intented for character-based resources) - + * * @return custom encoding or {@code null} to use default */ public String getTextEncoding() @@ -288,7 +286,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl if (resourceStream == null) { return sendResourceError(resourceResponse, HttpServletResponse.SC_NOT_FOUND, - "Unable to find resource"); + "Unable to find resource"); } // add Last-Modified header (to support HEAD requests and If-Modified-Since) @@ -307,27 +305,28 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl // set Content-Type (may be null) resourceResponse.setContentType(contentType); - + // set content encoding (may be null) resourceResponse.setTextEncoding(getTextEncoding()); + // supports accept range + resourceResponse.setAcceptRange(ContentRangeType.BYTES); + try { - // read resource data - final byte[] bytes = IOUtils.toByteArray(resourceStream.getInputStream()); + // read resource data to get the content length + long contentLength = IOUtils.toByteArray(resourceStream.getInputStream()).length; // send Content-Length header - resourceResponse.setContentLength(bytes.length); + resourceResponse.setContentLength(contentLength); + + // get content range information + Long startbyte = RequestCycle.get().getMetaData(CONTENT_RANGE_STARTBYTE); + Long endbyte = RequestCycle.get().getMetaData(CONTENT_RANGE_ENDBYTE); // send response body with resource data - resourceResponse.setWriteCallback(new WriteCallback() - { - @Override - public void writeData(Attributes attributes) - { - attributes.getResponse().write(bytes); - } - }); + resourceResponse.setWriteCallback(new PartWriterCallback( + resourceStream.getInputStream(), contentLength, startbyte, endbyte)); } catch (IOException e) { @@ -408,7 +407,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl { return internalGetResourceStream(getCurrentStyle(), getCurrentLocale()); } - + /** * locate resource stream for current resource * @@ -421,7 +420,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl /** * @return whether {@link org.apache.wicket.resource.ITextResourceCompressor} can be used to compress the - * resource. + * resource. */ public boolean getCompress() { @@ -466,8 +465,8 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl if (accept(realPath) == false) { throw new PackageResourceBlockedException( - "Access denied to (static) package resource " + absolutePath + - ". See IPackageResourceGuard"); + "Access denied to (static) package resource " + absolutePath + + ". See IPackageResourceGuard"); } if (resourceStream != null) @@ -478,11 +477,12 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl } /** - * An IResourceStream that processes the input stream of the original - * IResourceStream + * An IResourceStream that processes the input stream of the original IResourceStream */ private class ProcessingResourceStream extends ResourceStreamWrapper { + private static final long serialVersionUID = 1L; + private ProcessingResourceStream(IResourceStream delegate) { super(delegate); @@ -496,10 +496,12 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl try { bytes = IOUtils.toByteArray(inputStream); - } catch (IOException iox) + } + catch (IOException iox) { throw new WicketRuntimeException(iox); - } finally + } + finally { IOUtils.closeQuietly(this); } @@ -523,8 +525,8 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl /** * Checks whether access is granted for this resource. * - * By default IPackageResourceGuard is used to check the permissions but - * the resource itself can also make the check. + * By default IPackageResourceGuard is used to check the permissions but the resource itself can + * also make the check. * * @param path * resource path @@ -548,7 +550,8 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl */ public static boolean exists(final ResourceReference.Key key) { - return exists(key.getScopeClass(), key.getName(), key.getLocale(), key.getStyle(), key.getVariation()); + return exists(key.getScopeClass(), key.getName(), key.getLocale(), key.getStyle(), + key.getVariation()); } /** @@ -622,15 +625,13 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl return false; if (getClass() != obj.getClass()) return false; - + PackageResource other = (PackageResource)obj; - - return Objects.equals(absolutePath, other.absolutePath) - && Objects.equals(locale, other.locale) - && Objects.equals(path, other.path) - && Objects.equals(scopeName, other.scopeName) - && Objects.equals(style, other.style) - && Objects.equals(variation, other.variation); + + return Objects.equals(absolutePath, other.absolutePath) && + Objects.equals(locale, other.locale) && Objects.equals(path, other.path) && + Objects.equals(scopeName, other.scopeName) && Objects.equals(style, other.style) && + Objects.equals(variation, other.variation); } String getParentFolderPlaceholder() @@ -675,12 +676,11 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl return false; CacheKey cacheKey = (CacheKey)o; - - return Objects.equals(locale, cacheKey.locale) - && Objects.equals(path, cacheKey.path) - && Objects.equals(scopeName, cacheKey.scopeName) - && Objects.equals(style, cacheKey.style) - && Objects.equals(variation, cacheKey.variation); + + return Objects.equals(locale, cacheKey.locale) && Objects.equals(path, cacheKey.path) && + Objects.equals(scopeName, cacheKey.scopeName) && + Objects.equals(style, cacheKey.style) && + Objects.equals(variation, cacheKey.variation); } @Override http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java new file mode 100644 index 0000000..ca70470 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/PartWriterCallback.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +package org.apache.wicket.request.resource; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.wicket.protocol.http.servlet.ResponseIOException; +import org.apache.wicket.request.resource.AbstractResource.WriteCallback; +import org.apache.wicket.request.resource.IResource.Attributes; +import org.apache.wicket.util.io.Streams; + +/** + * Used to read a part of an input stream and writes it to the output stream of the response taken + * from attributes of the writeData method. + * + * @author Tobias Soloschenko + * + */ +public class PartWriterCallback extends WriteCallback +{ + private final InputStream inputStream; + + private final Long contentLength; + + private final Long startbyte; + + private Long endbyte; + + private int bufferSize; + + + /** + * Creates a part writer callback.<br> + * <br> + * Reads a part of the given input stream. If the startbyte parameter is not null the number of + * bytes are skipped till the stream is read. If the endbyte is not null the stream is read till + * endbyte, else to the end of the whole stream. If startbyte and endbyte is null the whole + * stream is read. + * + * @param inputStream + * the input stream to be read + * @param the + * content length + * @param startbyte + * the start position to read from (if not null the number of bytes are skipped till + * the stream is read) + * @param endbyte + * the end position to read to (if not null the stream is going to be read till + * endbyte, else to the end of the whole stream) + */ + public PartWriterCallback(InputStream inputStream, Long contentLength, Long startbyte, + Long endbyte) + { + this.inputStream = inputStream; + this.contentLength = contentLength; + this.startbyte = startbyte; + this.endbyte = endbyte; + } + + /** + * Writes the data + * + * @param attributes + * the attributes to get the output stream of the response + * @throws IOException + * if something went wrong while writing the data to the output stream + */ + @Override + public void writeData(Attributes attributes) throws IOException + { + try + { + OutputStream outputStream = attributes.getResponse().getOutputStream(); + byte[] buffer = new byte[getBufferSize()]; + + if (startbyte != null || endbyte != null) + { + // skipping the first bytes which are + // requested to be skipped by the client + if (startbyte != null) + { + inputStream.skip(startbyte); + } + + // If there are no end bytes given read the whole stream till the end + if (endbyte == null || Long.valueOf(-1).equals(endbyte)) + { + endbyte = contentLength; + } + + long totalBytes = 0; + int actualReadBytes; + + while ((actualReadBytes = inputStream.read(buffer)) != -1) + { + totalBytes = totalBytes + buffer.length; + long lowerBuffer = endbyte - totalBytes; + if (lowerBuffer <= 0) + { + buffer = resizeArray(buffer, actualReadBytes); + outputStream.write(buffer); + break; + } + else + { + outputStream.write(buffer); + } + } + } + else + { + Streams.copy(inputStream, outputStream, getBufferSize()); + } + } + catch (ResponseIOException e) + { + // the client has closed the connection and + // doesn't read the stream further on + // (in tomcats + // org.apache.catalina.connector.ClientAbortException) + // we ignore this case + } + } + + /** + * Reallocates an array with a new size, and copies the contents of the old array to the new + * array. + * + * @param oldArray + * the old array, to be reallocated. + * @param newSize + * the new array size. + * @return A new array with the same contents. + */ + private static byte[] resizeArray(byte[] oldArray, int newSize) + { + int oldSize = oldArray.length; + byte[] newArray = new byte[newSize]; + int minLength = Math.min(oldSize, newSize); + if (minLength > 0) + { + System.arraycopy(oldArray, 0, newArray, 0, minLength); + } + return newArray; + } + + /** + * Sets the buffer size used to send the data to the client + * + * @return the buffer size used to send the data to the client (default is 4096) + */ + public int getBufferSize() + { + return bufferSize > 0 ? bufferSize : 4096; + } + + /** + * Sets the buffer size used to send the data to the client + * + * @param bufferSize + * the buffer size used to send the data to the client + */ + public void setBufferSize(int bufferSize) + { + this.bufferSize = bufferSize; + } + +} http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java index b191c35..d4ad5b3 100644 --- a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java +++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsExtendedTestPage.java @@ -29,18 +29,19 @@ public class MediaTagsExtendedTestPage extends WebPage public MediaTagsExtendedTestPage() { - Video video = new Video("video", new MediaStreamingResourceReference( - MediaTagsTestPage.class, "dummyVideo.m4a")); + Video video = new Video("video", new PackageResourceReference(MediaTagsTestPage.class, + "dummyVideo.m4a")); // source tag - Source source = new Source("source","http://www.mytestpage.xc/video.m4a"); + Source source = new Source("source", "http://www.mytestpage.xc/video.m4a"); source.setMedia("screen and (device-width:500px)"); source.setType("video/mp4"); source.setDisplayType(true); video.add(source); // tack tag - Track track = new Track("track", new PackageResourceReference(MediaTagsTestPage.class,"dummySubtitles.vtt")); + Track track = new Track("track", new PackageResourceReference(MediaTagsTestPage.class, + "dummySubtitles.vtt")); track.setKind(Kind.SUBTITLES); track.setLabel("Subtitles of video"); track.setSrclang(Locale.GERMANY); http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java index e8dbdd8..edc3ab4 100644 --- a/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java +++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/media/MediaTagsTestPage.java @@ -31,8 +31,8 @@ public class MediaTagsTestPage extends WebPage { PageParameters pageParameters = new PageParameters(); pageParameters.set("test", "test"); - Audio audio = new Audio("audio", new MediaStreamingResourceReference( - MediaTagsTestPage.class, "dummyAudio.mp3"), pageParameters); + Audio audio = new Audio("audio", new PackageResourceReference(MediaTagsTestPage.class, + "dummyAudio.mp3"), pageParameters); audio.setAutoplay(true); audio.setControls(true); audio.setCrossOrigin(Cors.USER_CREDENTIALS); @@ -42,8 +42,8 @@ public class MediaTagsTestPage extends WebPage audio.setEndTime("10"); add(audio); - Video video = new Video("video", new MediaStreamingResourceReference( - MediaTagsTestPage.class, "dummyVideo.m4a")); + Video video = new Video("video", new PackageResourceReference(MediaTagsTestPage.class, + "dummyVideo.m4a")); PageParameters pageParameters2 = new PageParameters(); pageParameters2.add("test2", "test2"); video.setPoster(new PackageResourceReference(MediaTagsTestPage.class, "dummyPoster.jpg"), http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java ---------------------------------------------------------------------- diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java b/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java index 87d70f5..9055ff1 100644 --- a/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/media/Home.java @@ -21,7 +21,6 @@ import java.util.UUID; import org.apache.wicket.examples.WicketExamplePage; import org.apache.wicket.markup.head.CssHeaderItem; import org.apache.wicket.markup.head.IHeaderResponse; -import org.apache.wicket.markup.html.media.MediaStreamingResourceReference; import org.apache.wicket.markup.html.media.Source; import org.apache.wicket.markup.html.media.video.Video; import org.apache.wicket.request.mapper.parameter.PageParameters; @@ -50,7 +49,7 @@ public final class Home extends WicketExamplePage { // Internal video with several options - Video video1 = new Video("video1", new MediaStreamingResourceReference(Home.class, + Video video1 = new Video("video1", new PackageResourceReference(Home.class, "video.mp4")); video1.setAutoplay(false); video1.setControls(true); @@ -65,7 +64,7 @@ public final class Home extends WicketExamplePage Video video2 = new Video("video2"); video2.setPoster(new PackageResourceReference(Home.class, "novideo.gif")); - Source source2 = new Source("source2", new MediaStreamingResourceReference(Home.class, + Source source2 = new Source("source2", new PackageResourceReference(Home.class, "video.mp4")); // Need to be set to true to show the type source2.setDisplayType(true); @@ -86,7 +85,7 @@ public final class Home extends WicketExamplePage /* * // video with track - * Video video4 = new Video("video4", new MediaStreamingResourceReference(Home.class, "dummyVideo.m4a")); + * Video video4 = new Video("video4", new PackageResourceReference(Home.class, "dummyVideo.m4a")); * * // source tag * Source source4 = new Source("source4", "http://www.mytestpage.xc/video.m4a"); http://git-wip-us.apache.org/repos/asf/wicket/blob/f623ceaa/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java ---------------------------------------------------------------------- diff --git a/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java b/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java index 0566d0c..a790d0b 100644 --- a/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java +++ b/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java @@ -97,6 +97,32 @@ public abstract class WebResponse extends Response public abstract void setContentType(final String mimeType); /** + * Sets the content range of the response. If no content range is set the client assumes the + * whole content. Please note that if the content range is set, the content length, the status + * code and the accept range must be set right, too. + * + * @param contentRange + * the content range + */ + public void setContentRange(final String contentRange) + { + setHeader("Content-Range", contentRange); + } + + + /** + * Sets the accept range (e.g. bytes) + * + * @param acceptRange + * the accept range header information + */ + public void setAcceptRange(final String acceptRange) + { + setHeader("Accept-Range", acceptRange); + + } + + /** * Set the contents last modified time, if appropriate in the subclass. * * @param time @@ -138,19 +164,18 @@ public abstract class WebResponse extends Response } /** - * <a href="http://greenbytes.de/tech/tc2231/">Encodes</a> the value of the filename - * used in "Content-Disposition" response header + * <a href="http://greenbytes.de/tech/tc2231/">Encodes</a> the value of the filename used in + * "Content-Disposition" response header * * @param filename - * the non-encoded file name + * the non-encoded file name * @return encoded filename */ private String encodeDispositionHeaderValue(final String filename) { - return (Strings.isEmpty(filename) ? - "" : - String.format("; filename=\"%1$s\"; filename*=UTF-8''%1$s", - UrlEncoder.PATH_INSTANCE.encode(filename, "UTF-8"))); + return (Strings.isEmpty(filename) ? "" : String.format( + "; filename=\"%1$s\"; filename*=UTF-8''%1$s", + UrlEncoder.PATH_INSTANCE.encode(filename, "UTF-8"))); } /** @@ -213,9 +238,9 @@ public abstract class WebResponse extends Response /** * Make this response cacheable - * <p/> - * when trying to enable caching for web pages check this out: - * <a href="https://issues.apache.org/jira/browse/WICKET-4357">WICKET-4357</a> + * <p/> + * when trying to enable caching for web pages check this out: <a + * href="https://issues.apache.org/jira/browse/WICKET-4357">WICKET-4357</a> * * @param duration * maximum duration before the response must be invalidated by any caches. It should @@ -223,7 +248,7 @@ public abstract class WebResponse extends Response * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC-2616</a>. * @param scope * controls which caches are allowed to cache the response - * + * * @see WebResponse#MAX_CACHE_DURATION */ public void enableCaching(Duration duration, final WebResponse.CacheScope scope) @@ -248,7 +273,7 @@ public abstract class WebResponse extends Response // Set cache scope setHeader("Cache-Control", scope.cacheControl); - + // Set maximum age for caching in seconds (rounded) addHeader("Cache-Control", "max-age=" + Math.round(duration.seconds())); @@ -266,7 +291,8 @@ public abstract class WebResponse extends Response * href="http://palisade.plynt.com/issues/2008Jul/cache-control-attributes">here</a> or in <a * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC-2616</a>. */ - public static enum CacheScope { + public static enum CacheScope + { /** * use all caches (private + public) * <p/>