You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devicemap.apache.org by bd...@apache.org on 2012/05/31 17:19:09 UTC

svn commit: r1344766 - in /incubator/devicemap/trunk/prototypes/probefilter: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/devicemap/ src/main/java/org/apache/devicemap/probefilter/ src/main/java...

Author: bdelacretaz
Date: Thu May 31 15:19:08 2012
New Revision: 1344766

URL: http://svn.apache.org/viewvc?rev=1344766&view=rev
Log:
dynamic probes prototype

Added:
    incubator/devicemap/trunk/prototypes/probefilter/   (with props)
    incubator/devicemap/trunk/prototypes/probefilter/README.md
    incubator/devicemap/trunk/prototypes/probefilter/pom.xml
    incubator/devicemap/trunk/prototypes/probefilter/src/
    incubator/devicemap/trunk/prototypes/probefilter/src/main/
    incubator/devicemap/trunk/prototypes/probefilter/src/main/java/
    incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/
    incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/
    incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/
    incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/
    incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/filter/
    incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/filter/DeviceMapProbeFilter.java
    incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/
    incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/Main.java
    incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/TestServlet.java
    incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/
    incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/dmap_demo.html
    incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/dmap_probe.html

Propchange: incubator/devicemap/trunk/prototypes/probefilter/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu May 31 15:19:08 2012
@@ -0,0 +1,10 @@
+target
+bin
+*.iml
+*.ipr
+*.iws
+.settings
+.project
+.classpath
+.externalToolBuilders
+maven-eclipse.xml

Added: incubator/devicemap/trunk/prototypes/probefilter/README.md
URL: http://svn.apache.org/viewvc/incubator/devicemap/trunk/prototypes/probefilter/README.md?rev=1344766&view=auto
==============================================================================
--- incubator/devicemap/trunk/prototypes/probefilter/README.md (added)
+++ incubator/devicemap/trunk/prototypes/probefilter/README.md Thu May 31 15:19:08 2012
@@ -0,0 +1,39 @@
+# Apache DeviceMap dynamic probes prototype
+
+To run this, build the jar file with "mvn clean install", run it
+with java -jar and open http://localhost:8085.
+
+I have created this prototype to experiment with a device detection
+technique similar to that of http://detector.dmolsen.com/ and 
+https://github.com/yiibu/profile (though much less complete so far).
+
+The scenario is as follows:
+
+1. When a request comes in, check if it has a DMAP_p cookie.
+
+2. If yes, let the request go through. The contents of the DMAP_p
+   cookie describe the device features, and can be used both
+   on the server and client sides.
+
+3. If the cookie is not present, redirect to a "dynamic probes" 
+   page, which runs javascript code to detect client features, 
+   sets the DMAP_p cookie with the results, and redirects
+   to the originally requested page (path is passed as an URL
+   parameter).
+   
+That's it - the hard part is in the javascript features detection
+code, in this first version that's extremely basic, just to 
+demonstrate the idea. We might use modernizr and platform.js
+(and/or share detection code with the above projets that inspired
+this experiment) for more complete features detection.
+
+TODO:
+
+Implement more realistic/complete features detection code.
+
+Cache DMAP_p values based on user-agent (or digest of that).
+Maybe differentiate between "static" values that are tied to
+the device, and "context" values like window size which can
+change due to orientation or resizing. 
+
+Maybe define an exchange format for such cached values.
\ No newline at end of file

Added: incubator/devicemap/trunk/prototypes/probefilter/pom.xml
URL: http://svn.apache.org/viewvc/incubator/devicemap/trunk/prototypes/probefilter/pom.xml?rev=1344766&view=auto
==============================================================================
--- incubator/devicemap/trunk/prototypes/probefilter/pom.xml (added)
+++ incubator/devicemap/trunk/prototypes/probefilter/pom.xml Thu May 31 15:19:08 2012
@@ -0,0 +1,117 @@
+<?xml version="1.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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>apache</artifactId>
+        <version>9</version>
+        <relativePath />
+    </parent>
+
+    <groupId>org.apache.devicemap</groupId>
+    <artifactId>probefilter</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>probefilter</name>
+    <url>http://incubator.apache.org/devicemap</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <!-- Unpack required dependencies to include them and 
+                        make our jar self-containing -->
+                    <execution>
+                        <id>unpack-jetty</id>
+                        <goals>
+                            <goal>unpack-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <includeGroupIds>commons-io,org.mortbay.jetty,org.slf4j</includeGroupIds>
+                            <excludeTransitive>false</excludeTransitive>
+                            <outputDirectory>${project.build.directory}/classes</outputDirectory>
+                            <overWriteReleases>false</overWriteReleases>
+                            <overWriteSnapshots>false</overWriteSnapshots>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>org.apache.devicemap.probefilter.testserver.Main</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.4</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>jetty</artifactId>
+            <version>6.1.25</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.5.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.5.2</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

Added: incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/filter/DeviceMapProbeFilter.java
URL: http://svn.apache.org/viewvc/incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/filter/DeviceMapProbeFilter.java?rev=1344766&view=auto
==============================================================================
--- incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/filter/DeviceMapProbeFilter.java (added)
+++ incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/filter/DeviceMapProbeFilter.java Thu May 31 15:19:08 2012
@@ -0,0 +1,118 @@
+/*
+ * 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.devicemap.probefilter.filter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Servlet Filter that handles the DeviceMap dynamic probe logic:
+ * 
+ *  If a request comes without a DMAP_p cookie, redirect to the probing
+ *  page which will run javascript features detection code and set  
+ *  that cookie. That page is expected to redirect to the originally 
+ *  requested page when done running the probes.
+ *  
+ *  TODO: we might want to cache results based on previous requests
+ *  coming from the same user-agent. http://detector.dmolsen.com/ uses
+ *  *digests* of the User-Agent strings for that, might be a good idea
+ *  to avoid privacy concerns if that info is shared.
+ *  
+ *  TODO: we might want to store the devicemap data in a request
+ *  attribute to make it easy to access downstream.
+ *  
+ */
+public class DeviceMapProbeFilter implements Filter {
+
+    public static final String COOKIE_NAME = "DMAP_p";
+    public static final String FORCE_PARAM = "DMAP_force"; 
+    public static final String PROBE_PATH = "DMAP_probe.html"; 
+    
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    
+    public void init(FilterConfig cfg) throws ServletException {
+    }
+    
+    public void destroy() {
+    }
+
+    public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain) 
+            throws IOException, ServletException {
+
+        final HttpServletRequest request = (HttpServletRequest)sreq;
+        final HttpServletResponse response = (HttpServletResponse)sresp;
+        
+        final Cookie c = getCookie(request, COOKIE_NAME);
+        if(request.getParameter(FORCE_PARAM) != null) {
+            log.info("Sending {} due to {}", PROBE_PATH, FORCE_PARAM);
+            sendProbe(request, response);
+        } else if(c == null) {
+            // TODO watch for infinite loop...
+            if(request.getPathInfo().endsWith("/" + PROBE_PATH)) {
+                log.info("Sending {}", PROBE_PATH);
+                sendProbe(request, response);
+            } else {
+                log.info("No {} cookie in request, redirect to {}", COOKIE_NAME, PROBE_PATH);
+                final String nextPath = request.getPathInfo();
+                response.sendRedirect(request.getContextPath() + PROBE_PATH + "?next=" + URLEncoder.encode(nextPath, "UTF-8"));
+            }
+        } else {
+            log.info("Got {}={}", c.getName(), c.getValue());
+            chain.doFilter(sreq, sresp);
+        }
+    }
+    
+    private Cookie getCookie(HttpServletRequest r, String name) {
+        final Cookie [] cookies = r.getCookies();
+        if(cookies != null) {
+            for(Cookie c : cookies) {
+                if(name.equals(c.getName())) {
+                    return c;
+                }
+            }
+        }
+        return null;
+    }
+    
+    private void sendProbe(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        // TODO probe is a static page for now, we might want to build it from components/plugins
+        final String resourcePath = "/dmap_probe.html";
+        final InputStream page = getClass().getResourceAsStream(resourcePath);
+        if(page == null) {
+            throw new IOException("Resource not found: " + resourcePath);
+        }
+        response.setContentType("text/html");
+        response.setCharacterEncoding("UTF-8");
+        IOUtils.copy(page, response.getOutputStream());
+        response.getOutputStream().flush();
+    }
+}

Added: incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/Main.java
URL: http://svn.apache.org/viewvc/incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/Main.java?rev=1344766&view=auto
==============================================================================
--- incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/Main.java (added)
+++ incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/Main.java Thu May 31 15:19:08 2012
@@ -0,0 +1,43 @@
+/*
+ * 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.devicemap.probefilter.testserver;
+
+import org.apache.devicemap.probefilter.filter.DeviceMapProbeFilter;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.servlet.Context;
+import org.mortbay.jetty.servlet.FilterHolder;
+import org.mortbay.jetty.servlet.ServletHolder;
+
+/** Starts the test servlet + filter */
+public class Main {
+    public static final String DEFAULT_PORT = "8085";
+    
+    public static void main(String [] args) throws Exception {
+        final String portStr = args.length > 0 ? args[0] : DEFAULT_PORT;
+        final int port = Integer.parseInt(portStr);
+        
+        Server server = new Server(port);
+        Context root = new Context(server,"/",Context.SESSIONS);
+        root.addServlet(new ServletHolder(new TestServlet()), "/*");
+        
+        // TODO not sure what this value means
+        final int dispatches = 1;
+        root.addFilter(new FilterHolder(new DeviceMapProbeFilter()), "/*", dispatches);
+        server.start();    
+    }
+}

Added: incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/TestServlet.java
URL: http://svn.apache.org/viewvc/incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/TestServlet.java?rev=1344766&view=auto
==============================================================================
--- incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/TestServlet.java (added)
+++ incubator/devicemap/trunk/prototypes/probefilter/src/main/java/org/apache/devicemap/probefilter/testserver/TestServlet.java Thu May 31 15:19:08 2012
@@ -0,0 +1,46 @@
+/*
+ * 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.devicemap.probefilter.testserver;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+
+/** Stupid servlet that displays an information page */
+public class TestServlet extends HttpServlet {
+
+    private static final long serialVersionUID = 1L;
+
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        final String resourcePath = "/dmap_demo.html";
+        final InputStream page = getClass().getResourceAsStream(resourcePath);
+        if(page == null) {
+            throw new IOException("Resource not found: " + resourcePath);
+        }
+        response.setContentType("text/html");
+        response.setCharacterEncoding("UTF-8");
+        IOUtils.copy(page, response.getOutputStream());
+        response.getOutputStream().flush();
+    }
+}

Added: incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/dmap_demo.html
URL: http://svn.apache.org/viewvc/incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/dmap_demo.html?rev=1344766&view=auto
==============================================================================
--- incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/dmap_demo.html (added)
+++ incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/dmap_demo.html Thu May 31 15:19:08 2012
@@ -0,0 +1,114 @@
+<html>
+
+<!--
+
+    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.
+-->
+
+<head>
+<title>DeviceMap dynamic probes demo: target page</title>
+<link href="http://incubator.apache.org/devicemap/css/devicemap.css" rel="stylesheet" type="text/css">
+</head>
+
+<script language="javascript">
+
+// start devicemap client scope
+(function() {
+
+    var dmapClient = {};
+    
+    dmapClient.trim = function(str) {
+        return str.replace(/^\s+|\s+$/g, '');
+    }
+    
+    dmapClient.getCookieValue = function(name) {
+        var cooks = document.cookie.split(";");
+        var prefix = name + "=";
+        for(var i in cooks) {
+            var c = dmapClient.trim(cooks[i]);
+            if(c.indexOf(prefix) == 0) {
+                return dmapClient.trim(c.substring(prefix.length));
+            }
+        }
+    }
+    
+    dmapClient.dump = function(id) {
+        var e = document.getElementById(id);
+        if(e) {
+            e.innerHTML = dmapClient.getCookieValue("DMAP_p");
+        }
+    };
+    
+    // set onload handler
+    var oldOnload = window.onload;
+    window.onload = function() {
+        dmapClient.dump("dmap-dump");
+        if(oldOnload) {
+            oldOnload.call(window);
+        } 
+    }
+
+// end devicemap client scope 
+})();    
+</script>
+
+<body>
+
+<h1>DeviceMap dynamic probes demo - target page</h1>
+
+<p>
+
+<a href="http://incubator.apache.org/devicemap">
+<img align="right" src="http://incubator.apache.org/devicemap/images/devicemap-logo/devicemap-logo-300-98.png"/>
+</a>
+
+You should get here after accessing
+any URL on the test server, with the below device info containing device map
+information that's refreshed when the corresponding cookie expires (see
+<code>DMAP_cookieValiditySeconds</code> value in the below data).
+</p>
+
+<h1>Device map data</h1>
+
+<p>
+The following device data is read from a cookie that's set by the probe page, so
+it's accessible both on the client and server side.
+</p>
+
+<code id="dmap-dump">
+</code>
+
+<h1>More info</h1>
+
+<p>
+You can see the javascript code used to build this data in the source
+code of the <a href="/DMAP_probe.html?DMAP_force=true">DMAP_probe.html</a> page.
+</p>
+
+<p>
+The complete source code can be found at
+<a href="http://svn.apache.org/repos/asf/incubator/devicemap/trunk/prototypes/probefilter">https://svn.apache.org/repos/asf/incubator/devicemap/trunk/prototypes/probefilter</a>.
+</p> 
+
+<p>
+Thanks to <a href="http://detector.dmolsen.com/">http://detector.dmolsen.com/</a> and <a href="https://github.com/yiibu/profile">https://github.com/yiibu/profile</a> for inspiration.
+This prototype is well on its way to be YABFDL <em>(Yet Another Browser- and Feature-Detection Library)</em>...we should try to share the features detection code which is the hard
+part, the rest is just plumbing.
+</p>
+
+</body>
+
+</html>
\ No newline at end of file

Added: incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/dmap_probe.html
URL: http://svn.apache.org/viewvc/incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/dmap_probe.html?rev=1344766&view=auto
==============================================================================
--- incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/dmap_probe.html (added)
+++ incubator/devicemap/trunk/prototypes/probefilter/src/main/resources/dmap_probe.html Thu May 31 15:19:08 2012
@@ -0,0 +1,150 @@
+<html>
+
+<!--
+
+    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.
+-->
+
+<head>
+<title>DeviceMap dynamic probes demo: probes page</title>
+<link href="http://incubator.apache.org/devicemap/css/devicemap.css" rel="stylesheet" type="text/css">
+</head>
+
+<script language="javascript">
+
+// start devicemap code scope
+(function() {
+
+    // TODO short cookie validity for now, for testing
+    var dmapCookieValiditySeconds = 30;
+    
+    // Detector functions - we need more of those, see
+    // previous logcapture prototype which used modernizr
+    // and platform.js for that.
+    var detectors = {
+        elementSupport : function(elementName, propertyName) {
+            var e = document.createElement(elementName);
+            var value = eval("e." + propertyName) ? "true" : "false";
+            return value;
+        },
+        
+        // from http://en.wikipedia.org/wiki/XMLHttpRequest
+        xhr : function() {
+            var xhr = null;
+            try { xhr = new XMLHttpRequest(); } catch (e) {}
+            try { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
+            try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
+            return (xhr!=null);     
+        }
+    };
+
+    // Setup probes that we'll use to build the device map.
+    // We might use a simple plugin system for that.    
+    var probes = {
+        "DMAP_timestamp" : function() { return new Date().getTime(); },
+        "DMAP_cookieValiditySeconds" : function() { return dmapCookieValiditySeconds; },
+        
+        "navigator.vendor" : function() { return navigator.vendor; },
+        "navigator.platform" : function() { return navigator.platform; },
+        
+        "html5Audio" : function() { return detectors.elementSupport("audio","play") },
+        "html5Video" : function() { return detectors.elementSupport("video","play") },
+        
+        "xhr" : function() { return detectors.xhr(); },
+        
+        "TODO" : function() { return "Need to add more probes, see previous logcapture prototype, modernizr etc..." } 
+    };
+    
+    // The actual DeviceMap building code - avoid javascript
+    // libraries to keep things minimal for now 
+    var dmap = {};
+    
+    dmap.setCookie = function(name, value, validitySeconds) {
+        var exp = new Date();
+        exp.setTime(exp.getTime() + validitySeconds * 1000);
+        var c = name + "=" + value + "; expires=" + exp.toGMTString() + "; path=/";
+        document.cookie = c;
+    }
+    
+    dmap.decodeUrlParam = function(str) {
+        return decodeURIComponent(str.replace(/\+/g, ' '));
+    }
+    
+    // from http://snipplr.com/view/19838/get-url-parameters/
+    dmap.getUrlParams = function() {
+        var map = {};
+        var f = function(m,key,value) { map[key] = dmap.decodeUrlParam(value); };
+        var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, f);
+        return map;
+    }
+
+    // Run our probes, build and dump device map data    
+    dmap.probe = function() {
+        var dmapData = {};
+        
+        for(i in probes) {
+            dmapData[i] = probes[i].call(window);
+        }
+        
+        // Dump dmapData in cookie
+        // TODO do we need any encoding here?
+        var value = "{ ";
+        for(i in dmapData) {
+            value += '"' + i + '":';
+            value += '"' + dmapData[i] + '", ';
+        }
+        value += " }";
+        
+        dmap.setCookie("DMAP_p", value, dmapCookieValiditySeconds);   
+    };
+    
+    // Run our probes when document is loaded.
+    var oldOnload = window.onload;
+    window.onload = function() {
+        dmap.probe();
+        var params = dmap.getUrlParams();
+        if(params.next) {
+            setTimeout("document.location.href='" + params.next + "'", 2000);
+        }
+        if(oldOnload) {
+            oldOnload.call(window);
+        }
+    }
+
+// end devicemap code scope 
+})();    
+</script>
+
+<body>
+
+<h1>DeviceMap dynamic probes page</h1>
+
+<p>
+<a href="http://incubator.apache.org/devicemap">
+<img align="right" src="http://incubator.apache.org/devicemap/images/devicemap-logo/devicemap-logo-300-98.png"/>
+</a>
+
+You might briefly see this page, but
+the probe code should redirect to the target page after 2 seconds. 
+</p>
+
+<p>
+TODO: add link to redirect to the right place if this fails.
+</p>
+
+</body>
+
+</html>
\ No newline at end of file