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/>

Reply via email to