Thursday, 30 May 2013

Learning JavaScriptMVC Review

JavaScriptMVC is an open-source Rich Internet Application framework based on jQuery and OpenAjax. It extends those libraries with a model–view–controller architecture and tools for testing and deployment. As it does not depend on server components, it can be combined with any web service interface and server-side language.

Packt Publishing requested that I review their new book on the subject, Learning JavaScriptMVC by Wojciech Bednarski, available to buy from Packt's website here:
http://link.packtpub.com/hFZPlQ

The book starts with a preface which points out that the JavaScriptMVC framework is a client side only technology and gives an overview of the six chapters and what they cover, as well as the usual intended audience and conventions used in the book.

Chapter one starts with an introduction to JavaScriptMVC, giving details of its constituent parts: StealJS, FuncUnit, jQueryMX and DocumentJS. The chapter moves on to describe the advantages of using the JavaScriptMVC framework over other alternatives, and spends some time discussing the advantages and disadvantages of different web application architectures.

With the what and why of JavaScriptMVC laid out the author goes on to offer three methods of getting up and running with the framework:

  1. Download the complete package from the official website.
  2. Pull code and libraries from the authors GitHub.
  3. Use Vagrant to download and configure a ready to go virtual machine.

The author discourages using the first method for anything other than a fast try-out, recommends the second method for actual deployment, and advocates the third method for development as it provides an encapsulated development environment.

I'd never used Vagrant before, and liked the idea of a preconfigured and encapsulated dev environment, so I was exited to try out the third method. In retrospect I should have been suspicious of any method which requires you to download literally gigabytes of software to use a JavaScript framework.

Installing Vagrant and it's dependencies on my Linux Mint machine was pretty straight forward, just using:

apt-get install vagrant

VirtualBox is a dependency of Vagrant which was also installed. Next I downloaded the specified Vagrant configuration file and issued the command to fire up the preconfigured server and dev environment, very exciting:

vagrant up

Hundreds and hundreds of megabytes of downloads later and Vagrant reports that the server cannot start because VirtualBox required me to make changes to my BIOS settings.

Lets just think about this for a minute, installation of multiple software packages, a 1.3 GB virtual machine image download and a BIOS setting change just to run a client side JavaScript framework – and this is the recommended method to get started - absolute madness.

I persisted with the Vagrant method and continued to follow the instructions in chapter one to build a Todo list, until I noticed that changes I was making to the pages HTML and JavaScript files were not being reflected in the browser when refreshed.

After some Googling, I found this explanation and solution - clear the nginx cache in Vagrant:

http://jeremyfelt.com/code/2013/01/08/clear-nginx-cache-in-vagrant

The commands I executed in addition to the above information:-

vagrant ssh
vagrant@lucid64:~$ sudo vi /etc/nginx/nginx.conf
vagrant@lucid64:~$ sudo service nginx restart

Finally after getting a working dev environment I typed out the code in the chapter to get the basic foundation of the application. I fired up my browser to the URL specified and it didn't work.

I'd already wasted too much time on this method and decided to abandon Vagrant, and try method two - get the files from GitHub.

I quickly installed Apache web server on my machine and cloned the authors GitHib repository. I moved my source files and reloaded the project in my browser. It didn't work again.

Last but not least I decided to try the first installation method described by the author - downloading the complete JavaScriptMVC package from the official website. This method gave me an installation of JavaScriptMVC which worked. I would recommend this method.

I completed chapter one, typing out the remaining code for the application, only to find myself with an app which did nothing and reported no errors.

I also took the downloadable source code for the chapter provided by the author and ran it, only to achieve the same result.

At this point I have given up on the book, I can't waste any more time trying to get the code to work. There is no point in learning how to document and test an application which doesn't work.

This book has made me frustrated and angry and I couldn't recommend it any less. I can only assume this book is a cruel joke by the author make me feel stupid and unproductive. Hopefully I can still get some use from this book, come winter I will be using it as a £14.99 fire lighter.

You may find other reviews of this book less ranty:


Downloads From Packt

Sunday, 17 March 2013

Properties design pattern and Prototype-based programming in Java

After reading Steve Yegge's post about the properties design pattern and how it can be used to create a prototype-based object system, I thought I'd have a go at an implementation in Java to understand more.

If you haven't read the above post already, it definitely worth a read (or two), and makes everything below make more sense, give it a go.

First, I wanted to define the minimum API needed to get a working properties object as described in the Overview section. A basic properties interface:

Properties.java

package org.adrianwalker.propertiespattern;

interface Properties<N, V> {

  V get(N name);

  V put(N name, V value);

  boolean has(N name);

  V remove(N name);
}

I'll defer describing the implementation until the end, because as soon as you have the basic functionality done there is a load more you're going to want to add.

Once you've got your Properties implementation storing name/value pairs and inheriting from a prototype Properties object stored against a reserved key name, at that point, your going to want to add some way for your objects to simulate methods to manipulate the property values.

Because Java doesn't have first class functions, a methods functionality needs to be encapsulated using the Strategy pattern.

Strategy.java

package org.adrianwalker.propertiespattern;

interface Strategy<N, V, T, A> {

  T execute(Properties<N, V> properties, A... args);
}

In addition to the Strategy interface, I extended the basic Properties interface with an execute method to call a Strategy property:

ExecutableProperites.java

package org.adrianwalker.propertiespattern;

interface ExecutableProperites<N, V, T, A> extends Properties<N, V> {

  T execute(N name, A... args);
}

You're also probably going to want a way of returning an object's property names, possibly filtered with by a regex, or a globing pattern, or partial string match or some kind object comparison. So another extension to the Properties interface:

FilterableProperties.java

package org.adrianwalker.propertiespattern;

import java.util.Set;

interface FilterableProperties<N, V> extends Properties<N, V> {

  Set<N> filter(N filter);
}

And it would be good to have an interface for cloning prototype-based objects:

CloneableProperties.java

package org.adrianwalker.propertiespattern;

interface CloneableProperties<N, V> extends Properties<N, V>, Cloneable {

  CloneableProperties<N, V> clone();
}

Implementation

With all of that out of the way it's time for an implementation. This implementation stores property name/value pairs in a HashMap, and filters using regular expressions:

HashMapProperties.java

package org.adrianwalker.propertiespattern;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

public final class HashMapProperties<V, T, A> implements
        Properties<String, V>,
        ExecutableProperites<String, V, T, A>,
        CloneableProperties<String, V>,
        FilterableProperties<String, V> {

  public static final String PROTOTYPE = "prototype";
  private final Map<String, V> properties;

  public HashMapProperties() {

    this.properties = Collections.synchronizedMap(new HashMap());
  }

  private HashMapProperties(final Map<String, V> map) {

    this.properties = Collections.synchronizedMap(new HashMap<String, V>(map));
  }

  @Override
  public V get(final String name) {

    if (properties.containsKey(name)) {

      return properties.get(name);
    }

    if (properties.containsKey(PROTOTYPE)) {

      Properties<String, V> prototype =
              (Properties<String, V>) properties.get(PROTOTYPE);

      return prototype.get(name);
    }

    return null;
  }

  @Override
  public V put(final String name, final V value) {

    if (PROTOTYPE.equals(name) && !(value instanceof Properties)) {

      throw new IllegalArgumentException(
              "prototype must be an instance of Properties");
    }

    return properties.put(name, value);
  }

  @Override
  public boolean has(final String name) {

    if (properties.containsKey(name)) {

      return null != properties.get(name);
    }

    if (properties.containsKey(PROTOTYPE)) {

      Properties<String, V> prototype =
              (Properties<String, V>) properties.get(PROTOTYPE);

      return prototype.has(name);
    }

    return false;
  }

  @Override
  public V remove(final String name) {

    if (properties.containsKey(PROTOTYPE)) {

      Properties<String, V> prototype =
              (Properties<String, V>) properties.get(PROTOTYPE);

      if (prototype.has(name)) {

        properties.put(name, null);

        return prototype.get(name);
      }
    }

    if (properties.containsKey(name)) {

      return properties.remove(name);
    }

    return null;
  }

  @Override
  public HashMapProperties<V, T, A> clone() {

    return new HashMapProperties<V, T, A>(properties);
  }

  @Override
  public T execute(final String name, final A... args) {

    V property = get(name);

    if (property instanceof Strategy) {

      return ((Strategy<String, V, T, A>) property).execute(this, args);
    }

    return null;
  }

  @Override
  public Set<String> filter(final String regex) {

    Pattern pattern = Pattern.compile(regex);

    Set<String> filteredNames = new HashSet<String>();

    Set<String> names = properties.keySet();

    synchronized (properties) {
      for (String name : names) {
        if (!PROTOTYPE.equals(name) && pattern.matcher(name).matches()) {
          filteredNames.add(name);
        }
      }
    }

    if (properties.containsKey(PROTOTYPE)) {
      FilterableProperties<String, V> prototype =
              (FilterableProperties<String, V>) properties.get(PROTOTYPE);
      filteredNames.addAll(prototype.filter(regex));
    }

    return filteredNames;
  }
}

Example Usage

The following example shows how you might use the above implementation. A prototype Properties object is created with a 'greeting' property and a 'getGreeting' Strategy property. These properties are inherited by a new Properties object.

The Properties object is cloned and it's greeting property is overridden.

The cloned Properties object's properties are filtered to return the getter Strategy's property name, then this is executed for the Properties object and it's clone:

Example.java

package org.adrianwalker.propertiespattern;

import java.util.Set;
import static org.adrianwalker.propertiespattern.HashMapProperties.PROTOTYPE;

public final class Example {

  public static void main(final String[] args) {
 
    // create a prototype properties object with a greeting and a 
    // strategy which returns the greeting and an argument
 
    Properties prototype = new HashMapProperties();
    prototype.put("greeting", "Hello");
    prototype.put("getGreeting", new Strategy() {
      @Override
      public String execute(final Properties properties, final Object... args) {

        return String.format("%s %s", properties.get("greeting"), args[0]);
      }
    });
 
    // create a properties object which inherit the greeting and 
    // strategy from the prototype

    HashMapProperties properties = new HashMapProperties();
    properties.put(PROTOTYPE, prototype);

    // clone the properties object and override the greeting property

    HashMapProperties clone = properties.clone();
    clone.put("greeting", "Ahoy");

    // filter for the getter properties, then execute the getter
    // on each properties object with a message argument and
    // print the message

    Set<String> getters = clone.filter("get.*");
    for (String getter : getters) {
      System.out.println(properties.execute(getter, "World"));
      System.out.println(clone.execute(getter, "Sailor"));
    }
  }
}

The example above should print the output:

Hello World
Ahoy Sailor

Finally

I've not implemented half of the functionality mentioned in original post, and not a fraction of a fraction of the possible applications for this pattern. The above code and a set of unit tests are available for download at the bottom of this page.

If you need the level of flexibility to warrant using this pattern, then, to quote the original article "use a programming language better suited for implementing the pattern: ideally, one that supports it from the ground up". So expect some JavaScript related posts in the future.

Source Code

Usage

Compile the project with 'mvn clean install'.

Tuesday, 5 February 2013

Email Sky Router Public IP Address with Gmail in Python

As far as I know, Sky Broadband don't offer a static IP address. You're going to want a way of keeping track of your externally available dynamic IP, so you can access your machine from outside of your network.

The python script below scrapes your Sky router's status page for your current IP and emails it your Gmail account.

Remember to change the Gmail username and password variables from '????????' to your Gmail authentication credentials. Run this script periodically as a cron job, and as long as you have Gmail access you'll know your IP.

#!/usr/bin/python

import smtplib
from email.mime.text import MIMEText
import urllib2
import re

# router login page
router_login = "http://192.168.0.1"
# router status url
router_status = "http://192.168.0.1/sky_router_status.html"
# router logout page
router_logout = "http://192.168.0.1/sky_logout.html"
# default sky router username and password
router_username = "admin"
router_password = "sky"
# gmail email address
email = "????????@gmail.com"
# gmail password
email_password = "????????"
# gmail smtp settings
server = "smtp.gmail.com"
port = 587
# email subject
subject = "Server IP"

# login to router
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, router_login, router_username, router_password)
authhandler = urllib2.HTTPBasicAuthHandler(passman)
opener = urllib2.build_opener(authhandler)
urllib2.install_opener(opener)
urllib2.urlopen(router_login)

# get the first ip on the status page
ip = urllib2.urlopen(router_status).read()
ip = re.findall(r"[0-9]+(?:\.[0-9]+){3}", ip)
ip = ip[0]

# logout of router
urllib2.urlopen(router_logout)

# check for last ip address
f = open(".last_ip", "a+")
f.seek(0, 0)
last_ip = f.readline()

# has ip changed
if ip != last_ip:

  # store the new ip
  f.truncate(0)
  f.write(ip)

  session = smtplib.SMTP(server, port)

  session.ehlo()
  session.starttls()
  session.login(email, email_password)

  body = "http://" + ip

  msg = MIMEText(body)
  msg['Subject'] = subject
  msg['From'] = email
  msg['To'] = email

  session.sendmail(email, email, msg.as_string())
  session.quit()

f.close()

Sunday, 26 August 2012

Iterate over first n files in a directory with Java 6 and JNA on Linux

I needed to be able to iterate over the first n number of files in a directory, without bringing them all back at once using File.list(). This is simple enough to do in Java 7 using the new DirectoryStream interface, but I only had access to Java 6 for this project.

Similar functionality can be reproduced using calls to native operating system functions using Java Native Access.

This example works on Linux but could easily be adapted for Windows.

DirectoryStream.java

package org.adrianwalker;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.NoSuchElementException;

public final class DirectoryStream implements Iterable<File>, Closeable {

  private String dirName;
  private POSIX posix;
  private Pointer dirp;

  public DirectoryStream(final String dirName) {
    this.dirName = dirName;
    this.posix = (POSIX) Native.loadLibrary("c", POSIX.class);
  }

  @Override
  public Iterator<File> iterator() {

    return new Iterator<File>() {
      private boolean gotNext = false;
      private Dirent file;

      @Override
      public boolean hasNext() {

        if (!gotNext) {
          getNext();
        }

        return file != null;
      }

      @Override
      public File next() {

        if (!gotNext) {
          getNext();
        }

        if (null == file) {
          throw new NoSuchElementException();
        }

        gotNext = false;

        return new File(dirName, file.getName());
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException();
      }

      private void getNext() {
        if (null == dirp) {
          dirp = posix.opendir(dirName);
        }

        file = posix.readdir(dirp);

        gotNext = true;
      }
    };
  }

  @Override
  public void close() throws IOException {
    posix.closedir(dirp);
  }

  public interface POSIX extends Library {

    public Pointer opendir(String dirname);

    Dirent readdir(Pointer dirp);

    int closedir(Pointer dirp);
  }

  public static final class Dirent extends Structure {

    public long d_ino;
    public long d_off;
    public short d_reclen;
    public char d_type;
    public char[] d_name = new char[256];

    public String getName() {
      return getPointer().getString(8 + 8 + 2 + 1, false);
    }

    public long getInode() {
      return getPointer().getLong(0);
    }

    public void setUseMemory(Pointer p) {
      useMemory(p);
    }
  }
}

Example usage:-

Main.java

package org.adrianwalker;

import java.io.File;
import java.io.IOException;

public final class Main {

  private static final int MAX_FILES = 10;
  private static final String CURRENT_DIR = ".";
  private static final String PARENT_DIR = "..";

  public static void main(final String[] args) throws IOException {

    String dirName = "/tmp/files";
    DirectoryStream directoryStream = new DirectoryStream(dirName);

    int fileCount = 0;
    for (File file : directoryStream) {

      String name = file.getName();
      if (name.equals(CURRENT_DIR) || name.equals(PARENT_DIR)) {
        continue;
      }      
      
      if (fileCount++ == MAX_FILES) {
        break;
      }

      System.out.println(file);
    }
  }
}

Source Code

Usage

Build the project with 'mvn clean install' and run the Main class.

Sunday, 15 July 2012

Java EE 6 Cookbook for Securing, Tuning, and Extending Enterprise Applications Review

Java Enterprise Edition is Oracle's enterprise Java computing platform. The platform provides an API and runtime environment for developing and running enterprise software, including network and web services. Java EE extends the Java Standard Edition providing an API for object-relational mapping, distributed and multi-tier architectures, and web services.

Packt Publishing requested that I review one of their titles about Java EE: Java EE 6 Cookbook for Securing, Tuning, and Extending Enterprise Applications by Mick Knutson, available to buy from Packt's website.

Java EE 6 Cookbook for Securing, Tuning, and Extending Enterprise Applications is an OK book, the breadth of topics covered is vast, but outside of the scope of the book at times. It feels like the book is trying to be an introductory tutorial to most aspects of Java development. Not that this is a bad thing, lots of the topics covered are important to day to day Java development and the advice given is pragmatic. Using the information as a starting point to expand your knowledge would make you a Java jack of all trades.

I found parts of it very informative and other parts frustrating, overall I'd say its worth a read but might not be what you expect. It not a book about JEE development as much as about peripheral concerns around JEE development which you might encounter in a project.

The information is presented in recipes - how-to examples focusing on a specific JEE feature, or other topic, put together to form a cookbook. Each recipe is presented in a formulaic way, with information under the sub-headings: 'Getting ready', 'How to do it...', 'How it works...' and 'There's more'.

A Java EE 6 installation, application server, and an editor are requirements to reproduce the examples in the book. GlassFish application server is used in a number of recipes, and some recipes are specific to the IntelliJ IDEA editor, but for the majority of the book any IDE and app server could be used.

The code to accompany the book available as a download isn't great. The Maven project pom.xml files needed altering before the code would build, and source code examples for some chapters seemed to be altogether missing.

Chapter one highlights key changes in Java EE 6 and how new features will improve performance and ease of development. The chapter first covers old JEE APIs, showing examples of what older technologies in old applications might need replacing, and what to replace those older technologies with. Covered next are introductions to the specifications and APIs including, Context and Dependency Injection (CDI), EJB 3.1, JPA 2.0, JAX-RS 1.1, Sevlet 3.0, WebBeans 1.0, and JSF 2.0.

Chapter two covers JPA persistence, some good recipes in this chapter are the 'Understanding @CollectionTable' recipe, covering the new annotation in the JPA 2.0 specification, and the JPA audit pattern in recipe 'Auditing historical JPA Operations'. The chapter finishes with an introduction to profiling JPA operations with the IntelliJ IDEA profile. I've never gotten on board with IntelliJ IDEA, and much prefer NetBeans and its profiler.

Chapter three covers JEE security, starting with definitions of security terms and deployment descriptors for role definitions and mapping in GlassFish. Next is a pointlessly detailed description of Java EE authentication methods complete with activity diagrams and HTML request/response headers.

XML based authorization configuration is introduced, then new programmatic and annotation based security in the next recipe.

A recipe I found useful later in the chapter was 'Configuring Linux firewall rules' - using iptables to configure access to SSH on Linux. Although I found it informative, I can't help thinking it doesn't really belong in this book - and the same with the next recipe 'Securely obfuscating Java byte-code', I fail to see how obfuscating code makes an application any more secure.

Chapter 4 deals with testing. Testing is clearly an important topic, and consideration of testing strategies, frameworks, tools and libraries should part of any Java project, but I was surprised to see this as a chapter in this book. It's more of a general software development topic which probably needs a book in its own right, I'm not sure it should be in a book about Securing, Tuning and Extending Enterprise Java apps.

The chapter starts with a good recipe demonstrating the use of the Maven Surefire plugin to enable a remote debugging session.

The rest of the chapter gives examples of testing frameworks and libraries, not just applicable to JEE testing but also to Java SE. JUnit and DBUnit are used automate database testing, along with test object mocking libraries Mockito, Powermock and Hamcrest.

Selenium and soapUI testing tools are introduced at the end of the chapter, with some good information on how to integrate these tools into your build and test process with Maven.

Chapter 5 uses the Groovy and Scala scripting languages running on the JVM, integrated with existing Java code, to write more concise unit test code. There is also a Jython servlet recipe demonstrating how to create a simple web page. Although not strictly JEE related, I enjoyed these recipes and can see how using them to write less code can have productivity increases.

The rest of the chapter deals with Aspect Orient Programming with AspectJ and CDI, and starts by describing some AOP terms: Cross-cutting-concerns, Advice, Pointcut, Aspect Joinpoint and Weaving. Recipes include weaving AspectJ advice into your own code and using AspectJ with existing 3rd party libraries, and using CDI interceptors. The AspectJ recipes in this chapter are not limited to JEE programming, and would be applicable to Java SE projects also.

Chapter 6 goes a totally off track with mobile development recipes. Recipes cover mobile web frameworks, native code generators, native web runtime frameworks, development considerations for iOS, Objective-C, mobile development IDEs, Android development, online emulators and other totally random mobile development things which should have been way out of the scope of this book!

Chapter 7 explores configuration and management, the important recipes from this chapter cover the use of VisualVM to remotely interact with and monitor Tomcat and GlassFish servers using JMX.

There is a recipe in this chapter on the use of JRebel for rapid redeployment. JRebel is a non-free, closed source product, although a free 14-day trial download is available. I try not to use non-free, closed source software, and could not promote the use of this product on my blog.

With Chapter 8 the book finishes with some useful tutorials for using profiling tools to monitor JVM memory usage and utilities to monitor HTTP activity. It covers the use of VisualVM and the jstatd plugin, monitoring TCP connections with Netstat, proxying connection with TCPMon to observe HTTP traffic, and server monitoring with Munin.

Unfortunately the chapter finishes with another recipe for a closed source, non-free product, HTTP Debugger. Again a 14 day trial download is available, but as the software is not FOSS I did not use it.

Overall I think there are some good tips in this book, but some chapters just feel like filler. If you're not experienced already with JEE development, then this isn't the book to start to learn with. If you are experienced then some chapters will seem a bit basic, and I'm sure some will seem a bit off topic. But all JEE developers will take at least a handful of useful recipes from this book.

This is an OK book but not what I was expecting. I'm not sure if the author is trying to take systems approach to JEE development, or just picked some of his favorite topics on the periphery of JEE development and put them together. I'd make sure you have a flick through it before you buy.


Downloads From Packt

Friday, 29 June 2012

HTML5 Video Pseudostreaming with Java 7

Adapted from the byte range request servlet code from the The BalusC Code, here is a Java 7 pseudostreaming servlet for playing video using the HTML5 video tag.

The servlet takes the name of a video file to play as a request parameter and the byte range of the video to stream in the Range header field.

PseudostreamingServlet.java

package org.adrianwalker.pseudostreaming;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardOpenOption.READ;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public final class PseudostreamingServlet extends HttpServlet {

  private static final int BUFFER_LENGTH = 1024 * 16;
  private static final long EXPIRE_TIME = 1000 * 60 * 60 * 24;
  private static final Pattern RANGE_PATTERN = Pattern.compile("bytes=(?<start>\\d*)-(?<end>\\d*)");
  private String videoPath;

  @Override
  public void init() throws ServletException {
    videoPath = getInitParameter("videoPath");
  }

  @Override
  protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
    processRequest(request, response);
  }

  private void processRequest(final HttpServletRequest request, final HttpServletResponse response) throws IOException {

    String videoFilename = URLDecoder.decode(request.getParameter("video"), "UTF-8");
    Path video = Paths.get(videoPath, videoFilename);

    int length = (int) Files.size(video);
    int start = 0;
    int end = length - 1;

    String range = request.getHeader("Range");
    Matcher matcher = RANGE_PATTERN.matcher(range);
    
    if (matcher.matches()) {
      String startGroup = matcher.group("start");
      start = startGroup.isEmpty() ? start : Integer.valueOf(startGroup);
      start = start < 0 ? 0 : start;

      String endGroup = matcher.group("end");
      end = endGroup.isEmpty() ? end : Integer.valueOf(endGroup);
      end = end > length - 1 ? length - 1 : end;
    }

    int contentLength = end - start + 1;

    response.reset();
    response.setBufferSize(BUFFER_LENGTH);
    response.setHeader("Content-Disposition", String.format("inline;filename=\"%s\"", videoFilename));
    response.setHeader("Accept-Ranges", "bytes");
    response.setDateHeader("Last-Modified", Files.getLastModifiedTime(video).toMillis());
    response.setDateHeader("Expires", System.currentTimeMillis() + EXPIRE_TIME);
    response.setContentType(Files.probeContentType(video));
    response.setHeader("Content-Range", String.format("bytes %s-%s/%s", start, end, length));
    response.setHeader("Content-Length", String.format("%s", contentLength));
    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

    int bytesRead;
    int bytesLeft = contentLength;
    ByteBuffer buffer = ByteBuffer.allocate(BUFFER_LENGTH);

    try (SeekableByteChannel input = Files.newByteChannel(video, READ);
            OutputStream output = response.getOutputStream()) {

      input.position(start);

      while ((bytesRead = input.read(buffer)) != -1 && bytesLeft > 0) {
        buffer.clear();
        output.write(buffer.array(), 0, bytesLeft < bytesRead ? bytesLeft : bytesRead);
        bytesLeft -= bytesRead;
      }
    }
  }
}

The location of the videos on the file system is configurable in the web.xml via the videoPath parameter for the stream servlet.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <servlet>
        <servlet-name>stream</servlet-name>
        <servlet-class>org.adrianwalker.pseudostreaming.PseudostreamingServlet</servlet-class>
        <init-param>
            <param-name>videoPath</param-name>
            <param-value>/tmp/videos</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>stream</servlet-name>
        <url-pattern>/stream</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

Some simple HTML5 uses the servlet as the source for the video tag. The name of the video files is provided by the video URL parameter.

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>HTML5 Video Pseudostreaming</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <div>
      <video id="video" controls>  
        <source src="stream?video=video.webm" />  
      </video>
    </div>
  </body>
</html>

You're going to need a video to play, so download a trailer or something:

http://trailers.apple.com/movies/sony_pictures/theamazingspiderman/amazingspiderman-tlr2b-USA_h480p.mov

The QuickTime trailer video file isn't supported by my target browser, so I need to convert it to another format, for this example WebM. ffmpeg can be used for the conversion:

ffmpeg -i amazingspiderman-tlr2b-USA_h480p.mov /tmp/videos/video.webm

Source Code

Usage

Run the project with 'mvn clean install jetty:run-war' and point your brower at http://localhost:8080/pseudostreaming.