» FasterDDR

welcome

Welcome to the FasterDDR open source project. The project provides read/write and refactor/caching interfaces to WURFL and OpenDDR (experimental) XML files and is written in Java.

overview

The most important features of this project are

FasterDDR is helpful for CPU- and memory-constrained environments which need access to the WURFL capabilities.

reading

Reading capabilities is straightforward:

// create device repository factory
DDRFactory ddrFactory = new WurflFactory();

// create repository instance
DDR ddr = ddrFactory.newInstance(new File("wurfl.xml"), "UTF-8");

// add patch file
ddrFactory.parsePatch(ddr, new File("wurfl_patch.xml"), "UTF-8");

// get a device using its id
Device device = ddr.getCapabilityDevice("nokia_n95_ver1");

// get the bandwidth as a string
String bandwidthString = device.getStringCapability("max_data_rate");

Alternatively, use one of the convenience methods:

// get the bandwidth as a long with a default value fallback
long bandwidth = device.getLongCapability("max_data_rate", -1L);

Note that zero-length strings ("") returns as null.

writing

Slashing the capabilities you do not need can save a lot of memory, given the DOM-nature of most DDR-APIs.

// get the device description repository and output stream
DDR ddr = getDDR();
OutputStream = getOutputStream();

// get serializer
WurflSerializer serializer = new WurflSerializer();

// add group
serializer.addGroup("object_download");

// add capability to group
serializer.addCapability("object_download", "video_mov");

// add another group
serializer.addGroup("display");

// add capability to group
serializer.addCapability("display", "max_image_width");

// serialize
serializer.serialize(ddr, out);

In addition, it is also possible to add a device filter to remove devices you do not need.

refactoring

Refactoring device description repository files can potentially reduce the size of the output XML file further.

// get the device description repository
DDR ddr = getDDR();

// create a set of the desired output capabilities
Set capabilities = new HashSet();
capabilities.add("streaming_mp4");
capabilities.add("streaming_mov");
capabilities.add("streaming_3g2");
capabilities.add("streaming_3gpp");
		
// create a capability filter
CapabilityFilter capabilityFilter = new CapabilityFilterImpl(capabilities);
		
// create refactorer				
DDRRefactorer refactorer = new WurflRefactorer();

// refactor		
DDR refactoredDDR = refactorer.refactor(ddr, capabilityFilter);

Refactoring reorganizes the device hierarchy, but keeps everything else intact, like device id, user-agent and capability values.
Refactoring should be performed offline, and patches must be incorporated first. Again, a device filter can also used.

example results
Refactoring the capabilities found within each WURFL group, the following reductions were made:

Group name Capabilities Before After Change
ajax 9 2098 KB 2259 KB -8 %
bearer 5 2192 KB 2545 KB -14 %
bugs 4 2095 KB 2196 KB -5 %
cache 2 2105 KB 2196 KB -5 %
chtml_ui 7 2070 KB 2198 KB -6 %
css 5 2109 KB 2251 KB -7 %
display 9 2595 KB 4079 KB -37 %
drm 3 2149 KB 2373 KB -10 %
flash_lite 7 2093 KB 2354 KB -12 %
html_ui 11 2120 KB 2225 KB -5 %
image_format 14 2231 KB 3190 KB -31 %
j2me 77 2275 KB 2874 KB -21 %
markup 19 2259 KB 2754 KB -18 %
mms 44 2298 KB 4239 KB -46 %
object_download 79 2680 KB 4011 KB -34 %
pdf 1 2072 KB 2206 KB -7 %
playback 20 2189 KB 2757 KB -21 %
product_info 19 2564 KB 2953 KB -14 %
rss 1 2061 KB 2199 KB -7 %
security 2 2053 KB 2197 KB -7 %
sms 29 2140 KB 2222 KB -4 %
sound_format 21 2255 KB 3167 KB -29 %
storage 10 2256 KB 2465 KB -9 %
streaming 17 2209 KB 2898 KB -24 %
transcoding 2 2054 KB 2196 KB -7 %
wap_push 14 2190 KB 2539 KB -14 %
wml_ui 22 2186 KB 2517 KB -14 %
wta 5 2156 KB 2425 KB -12 %
xhtml_ui 34 2259 KB 2381 KB -6 %

Note that: Results will vary depending on which capabilities you include - as with all optimizations, care should be taken in
verification of the actual improvement.

caching

The project also includes a simple cache filter which helps in populating a delegate cache provided by you. It organizes
devices which do not specify their own capabilities in sets with their parent(s). As one device is assigned a value,
all members in the same set are assigned that value too.

// get the device description repository
DDR ddr = getDDR();

// get your usual cache from somewhere, for your custom value V
CacheProvider<String, V> cacheDelegate = getCache();

// create the cache factory
DDREmptyDescendantCacheFactory factory = new DDREmptyDescendantCacheFactory(ddr);

// get a cache instance
EmptyDescendantCache<V> cache = factory.newEmptyDescendantCache(cacheDelegate);

// as a request is processed, check cache for some device id
V value = cache.get("nokia_n95_ver1");

if(value == null) { // no value is cached
   // generate a new value
   value = createValue("nokia_n95_ver1");
   
   // add value to cache
   cache.put("nokia_n95_ver1", value); // populates all devices in the same groups as nokia_n95_ver1
}

// .. use value in request response

In other words

cache.put("nokia_n95_ver1", value);

can later yield a positive result for

V value = cache.get("nokia_n93_ver1");

if the two devices are in the same set. Use a cache delegate wrapper if you want to use a more structured or complicated
key in your actual backend cache, i.e. for example go from 'nokia_n95_ver1' to '/items/54/nokia_n95_ver1'.

The cache is light-weight and suitable as part of for example EAR or WAR deployment initializations.
There is also a version of this cache for use with the latest WURFL API.

combining cache and refactoring - example results
Refactoring typically increases the number of empty devices, thus adding to the cache effectiveness.
The following tables shows cache statistics before and after refactoring, again for each WURFL group:

Sets Empty devices Requests
Group Before After Before After Before After
ajax 124 22 6555 6679 172 15
bearer 492 28 4652 6672 8254 32
bugs 2 2 6684 6686 5 5
cache 5 3 6681 6685 4 4
chtml_ui 10 6 6680 6683 16 7
css 227 7 6116 6684 1712 12
display 1115 1156 1276 4740 13994 10054
drm 453 8 5456 6682 5127 12
flash_lite 238 36 5666 6662 4223 42
html_ui 100 20 6490 6678 120 22
image_format 973 133 2873 6580 11681 248
j2me 776 291 4493 6463 8308 921
markup 598 167 3754 6563 10193 349
mms 637 490 4171 6084 9094 3518
object_download 974 726 3441 5674 10743 6876
pdf 70 2 6563 6686 21 1
playback 581 187 4981 6548 6950 344
rss 33 2 6646 6686 1 1
security 14 2 6669 6686 17 1
sms 156 21 6442 6672 107 15
sound_format 913 416 3478 6384 10556 1696
storage 689 182 4190 6564 9058 358
streaming 1077 218 1910 6516 13240 454
transcoding 3 3 6686 6685 1 1
wap_push 474 51 4619 6651 8113 39
wml_ui 416 29 4583 6670 8188 26
wta 393 9 5046 6682 6711 14
xhtml_ui 371 155 5755 6587 3858 229

Again note that: Results will vary depending on which capabilities you include.
The most important number in the above table is the estimated number of (random, uniformly distributed) requests for (6686 root) devices
required to fill 90% of the cache, before and after refactoring. Refactoring makes the number of empty (root) devices
increase and thus the number of sets go down correspondingly. Then the cache is likely to be populated with fewer requests,
increasing hit rates and so increasing performance.

other features

We managed to squeeze in a few extra essential features:

See the javadoc for additional documentation.

license

The FasterDDR library is released under the MIT license.

feedback

Please tell us what you think! Use the forums or mailing lists. All feedback is valuable.

weblog