Lets set up Spring MVC

Welcome to the Modern Web, here’s your library. Spring MVC comes standard with the monolith that is the Spring Framework. It certainly makes creating a web app easy… once it’s set up. Getting started, though, is the difficult part.

This isn’t meant to be a very educational article, only an instructive article. If you are unfamiliar with the concepts listed, google is your friend. There are a lot of moving pieces to this, so lets dive in.

Requirements

What do we want to accomplish? We want to set up Spring MVC to handle all *.html files, not interact with resources such as javascript and CSS files, forward rendering to JSP files under WEB-INF/jsp and use Logback for logging.

Dependencies

There’s one downside for Spring, a lot of jars are involved. To accomplish the above we need the following jars:

aopalliance-1.0.jar
commons-logging-1.1.3.jar
groovy-all-2.1.1.jar
jcl-over-slf4j-1.7.5.jar
jstl-1.2.jar
logback-classic-1.0.13.jar
logback-core-1.0.13.jar
slf4j-api-1.7.5.jar
spring-aop-3.2.3.RELEASE.jar
spring-beans-3.2.3.RELEASE.jar
spring-context-3.2.3.RELEASE.jar
spring-core-3.2.3.RELEASE.jar
spring-expression-3.2.3.RELEASE.jar
spring-web-3.2.3.RELEASE.jar
spring-webmvc-3.2.3.RELEASE.jar

Your versions may vary

File Structure

We will order our files in the following directory structure:

\---src
    +---main
    |   +---groovy
    |   |   \---com
    |   |       \---mycom
    |   |           \---<controller>.groovy
    |   \---resources
    |       \---logback.xml
    \---webapp
        +---css
        +---images
        +---js
        \---WEB-INF
            +---web.xml
            +---spring-servlet.xml
            \---jsp
                \---<view>.jsp

<controller>.groovy will hold the controller code that will load the model and forward to the <view>.jsp under WEB-INF/jsp. logback.xml will configure our logging to print out all the debug statements to make sure the app is working correctly. css, images and js hold the appropriate resource files. spring-servlet.xml configures Spring MVC to find our controllers and views. And web.xml is the standard deployment descriptor.

web.xml

The deployment descriptor sets up 4 items: the welcome files list, set to index.html, default HTML escaping to prevent XSS attacks, the DispatcherServlet, and which files Spring MVC will handle.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>

  <!-- Escape HTML output by default -->
  <context-param>
    <param-name>defaultHtmlEscape</param-name>
    <param-value>true</param-value>
  </context-param>

  <!-- This servlet named "spring" will make Spring MVC look for
       spring-servlet.xml under WEB-INF -->
  <servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <!-- Map Spring MVC to all .html files -->
  <servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>*.html</url-pattern>
  </servlet-mapping>
</web-app>

spring-servlet.xml

The *-servlet.xml file is automatically loaded by the DispatcherServlet based on the servlet name. In this case, Spring will look for spring-servlet.xml. We need to configure this file to do the following: scan our source code for controllers, note that Spring MVC will be driven by annotations (not necessarily needed, but will prevent issues in the future) and tell Spring MVC to resolves views in our WEB-INF/jsp folder.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:mvc="http://www.springframework.org/schema/mvc"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

  <!-- All annotated Spring classes under this package will be picked up -->
  <context:component-scan base-package="com.mycom"/>
  <!-- Drive Spring MVC with annotations -->
  <mvc:annotation-driven />

  <!-- Any view returned by the app will be resolved to WEB-INF/lib/[view].jsp -->
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
  </bean>
</beans>

Resources

Since Spring MVC is mapped to *.html, only html files will be picked up. So any images, CSS and javascript can be placed in their respective folders and be picked up with out being processed.

Controllers

Any groovy files with the @Controller annotation will be picked up Spring. The controller needs to hold methods that map to a specific URL (an html file, in this case). It will then need return a Model and or View. The string representing the view will be mapped to the corresponding JSP file under WEB-INF/jsp.

package com.mycom.controller

import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.servlet.ModelAndView

// Ensure Spring picks this class up as a controller
@Controller
class DefaultController {
  // map index.html to WEB-INF/jsp/index.jsp
  @RequestMapping('/index.html')
  def index() {
    return 'index'
  }

  // map search.html to WEB-INF/jsp/search.jsp with a SearchResults as the model
  @RequestMapping('/search.html')
  def search() {
    return new ModelAndView('search', 'results', new SearchResults(...))
  }
}

Views

Our views a simple JSP pages that get resolved to WEB-INF/jsp/.jsp. Since this is just an article about setup, you will need to see the documentation for more information.

logback.xml

Lastly we configure our logging to print out all our debug statements, letting us know if everything is working correctly.

<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="true">
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>
        %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
      </pattern>
    </encoder>
  </appender>

  <logger name="org.springframework" level="DEBUG"/>
  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

Security and Usability

With respect to my last post, cars are actually quite easy to learn and use despite their extreme complexity. A driver can easily move from one vehicle to the next and comfortable operate it with only a quick look at the controls. This made possible by the fact that each car is trying to solve the same problem within the same limitations of size, road construction and regulations.

Security frameworks for programmers have a different set of problems that hinder usability greatly.

They need to solve a great number of security issues: XSS, CSRF, encryption, encoding, file indirection, logging, etc. All these issues are so unrelated that single framework which covers them all would be horrible to learn.

A framework that specializes in a single solution space such as CSRFGuard from OWASP can still have usability issues as they are trying to solve a difficult problem after an application has been built.

What developers need is security to be handled below the application level - within their MVC framework or even the language itself. This is needed as security should not be the job of the application programmer. Apps should not need to be designed around security issues. They should be designed around business requirements and security just comes along for the ride.

As framework designers, we need to remember that security is a necessary evil that adds no e real product value. It’s insurance that you pay up front to prevent possible issues in the future. In fact, security done right will always look like a waste if time - you’ve prevented attacks but no one will realize it as they will never see the actual threats.

I would really like to see OWASP and other security minded organizations contribute more security code to popular MVC frameworks like Spring MVC or JSF instead of creating new third party frameworks that can only work once the application exists.

Both security and car design have large, complex problems to solve and both need increased usability, but security is an always changing field with so many different problems to solve that a common design will never emerge. If we want programmers to secure their applications, then we need to make security “just work”. Given to choice between preventing possible attacks at vague times in the future or shipping a new product that will earn the company money, we all know which option will be chosen.

Let's Have a Little Talk About Usability

One of the most interesting things about cars, from a usability point of view, is how easy it is to drive one. Once you’ve learned how to drive one, nearly any other car or truck is easy to learn But take a moment and think about what is actually involved in controlling a 4 ton piece of metal at 60 miles per hour next to other large, fast moving vehicles and you may start feeling overwhelmed.

It really is a tribute to auto manufacturers and their designers that so many different designs could be so easily accessible to drivers with little introduction to the different designs. Considering that the majority of operations within a car require the driver to execute the action without ever looking, automobiles should really be looked at as a pinnacle of design.

However, I’ve recently been seeing an ad on Hulu (which I can’t find anywhere) comparing a car to a tablet. The announcer asks the viewer to compare the number of buttons on their car stereo and on their tablet and implores them to enter the future by getting rid of all that old-school, tactile feedback and switch to something requires eye sight for every operation.

Every time I see this ad, I shake my head and cry a little - on the inside. Controlling a car by touchscreen is one of the stupidest and dangerous ideas I can think of. Now, instead of keeping your eyes on the road and changing the radio station by touch alone, you now need to look at your dashboard to find exactly where your finger, while hoping that the touchscreen was responsive enough to detect your selection.

Look at typing on your tablet as an example. If you’re used to touch typing, tablet typing is painfully slow and only auto-correct will save you. And unless the build in autonomous driving into new cars, there won’t be an equivalent to vehicular auto-correct any time soon.

It’s one thing to take a known, good design and throw it out the window - this could even be considered brave and daring in seeing how new features work with their users - but removing usability and pretending it’s the main selling point in your product really gets under my skin. Learn from existing usability problems with tablets first and try improve your current design instead of going with what would be considered “cool”.

There is No Such Thing as Two

Years ago, I don’t know where, I don’t know when, I heard the statement “There is no such thing as two.” It was about programming in general, and the definition went something like this:

  • You can do something zero times.
  • You can do something one time.
  • You can do something many times.

And that’s it. You can never do something twice in programming because there is a good chance that once you do something twice, you’ll do it a third or fourth time.

This sentiment comes up in many places like the famous DRY Principal which uses a bit more formal definition of ensuring all pieces of information have a single source of truth.

But DRY is usually meant for data. On the processing side, Once and Only Once is more appropriate. If you ever see that you’ve repeated yourself, abstract out the similar functionality and call the common code instead.

Even though the same idea is hidden within each of the above statements, I still prefer thinking of the number two as an impossible object.

For one thing, the definition is clearly stated in common words and doesn’t try to be academic.

Secondly, the three bullets have hidden meanings found in other coding best practices.

  • You can do something zero times - This is obvious, but drawing it out and stating it explicitly hints at optimization. Any time you refactor code, you should trying to get rid of the parts that are no longer needed.
  • You can do something once - There is no need to prematurely optimize and abstract your code. Wait until you know that a new method or class is needed before increasing the complexity of your code base.
  • You can do something many times - Here is the heart of “no such thing as two”. Once you’ve done something more than once, you should be able to do it as many times as you want.

Lastly, the geekier side of me likes the play on binary. You can only use the digits 0 and 1, but you can represent any number you wish. You’ll never see a 2.

And, as we all know, seeing a 2 can be scary.

Spammers Be Spamming

It’s bad enough that you are spamming me, but did you really have to call my posts “rife with spelling errors”? Even if it is true, that’s just not nice Mr. Porn spammer.

The New Console Wars

With the release of the XBox One, the new console wars have finally begun. Depending on whom you talk to, the reaction is either a big yawn or an enraged scream.

Of the three big consoles, none really hit the niche I’m looking for. As a parent of a toddler, who isn’t allowed to watch TV, and a newborn, I don’t have much time to actually play console games. Instead, we use a PS3 as a media center. The XBox 360 and Wii are both collecting dust.

Wii U’s ability to play games on a handheld screen is really appealing to me. If my wife wants to watch Grey’s Anatomy, I could still fit in some play time. However, the only good games released on the Wii were existing Nintendo properties, and there is only so much Mario I can play before I get sick of it.

Sony wants to focus on social with their PS4. I personally can stand social. The only reason I’m on Facebook is so I can update friends and family on my kids’ lives, not mine. I don’t want to automatically post about whatever game I’m playing as none of my friends care about gaming.

Microsoft’s XBox One is the most intriguing as they are gearing it towards the media hub that I want. I use my PS3 as a media hub all the time. However, instead of having a cable subscription, we use PlayOn and Netflix to watch all our shows. The new XBox doesn’t really give me anything new. Controlling live television with Kinect and being able to swap between TV and games instantly are great, but Microsoft insists on charging for services to which I already subscribe and will use far more than Live TV or gaming. I loved Netflix on the 360 until my gold subscription ran out, hence the reason I use PS3 now instead of my XBox.

Each console seems to offer a piece of what I’m looking for in a gaming console and media center, but they all have downsides that will keep me away. I suppose it doesn’t matter much at this point; I won’t be purchasing a new console until the inevitable price drop anyway.

Zelda's Phantom Menace

About a week ago, I started playing The Legend of Zelda: Phantom Hourglass, a DS game from 2007. Now, at about the halfway point, I feel it is important to note the annoying design choices in the game. Specifically: bad stylus controls, the Phantom Hourglass dungeon itself, and pointless challenges.

Everything in this game must be done using the touch screen. There is no other option. Want to move Link? Press in the direction he Gould move. Want to attack? Swipe or tap. Want to change weapons? Press on the menu. Want to advance through these boring conversations? Keep tapping until everyone shuts up.

There is no reason that every action needs to be done with the stylus. This design makes it impossible to attack while moving or to quickly change between secondary weapons. It’s also causing my touchscreen to be scratched. And after The World Ends With You, I’m getting tired of game design decisions causing damage to the console.

Secondly, the title’s Phantom Hourglass is named after an item repeatedly used in the game giving you an artificial time limit in a central dungeon.

Puzzle solving under a time limit is bad enough, but this game also forces you to solve the same puzzles over and over. You visit the dungeon, make it to the next treasure, go explore, then return to the dungeon and repeat all the same. Puzzles until you make it to a new floor. Eventually, you get a warp to the middle of the dungeon, but its too late. What should be a fun part of the game turns tedious.

The second reason the dungeon is a pain is that all the enemies are invulnerable. Instead of a normal, action focused Zelda, you are suddenly forced onto a stealth game that doesn’t fit with the rest of your playthrough. Zelda and stealth are no strangers, but combine it with replaying the same dungeon repeatedly with a time limit and my patience wanes.

Lastly, the pointless but required side tracking to advance the plot. The best example so far is what is needed to access the Goron dungeon. You must talk with the leader who will only grant you access once you talk to every character on the island. OK, fine. Talking the leader again, you need to pass his quiz, with unrelated questions like “How many children are on the island?” and “What’s in this spot?”

Just let me save sage Tetra already! After getting 6 questions correct, you would think that you could now access the temple. You would be wrong. Instead of just walking directly to the right and into the dungeon, you need to play hide and seek with a kid first. He runs off to one side of the island where you must find him, then he runs to the temple entrance. I have no idea why that would be included in the game other than throwing one last frustration at the player for the hell if it.

It is only because I’m stubborn that I’ve made it this far into the game, but any more crap like this and it’s getting chucked out the window.

You should be ashamed of yourself

Computer Science, as a field, is relatively young. New advances are being created daily; both the theory and implementation are evolving at a rapid rate.

The tools we use, both hardware and software, are also changing. Not too long ago everyone programmed with 80 character wide screens. Now, wide 1080p screens may be considered too small.

Looking back at how we worked a mere 10 years ago is both frustrating and embarrassing. Similarly, looking back at your own work from only one year ago should make you wonder what you were thinking at the time. More than once, I’ve reviewed old code of mine and was unable to recognize the style.

If you look at your previous work and say, “I wouldn’t change a thing”, then you haven’t grown as a programmer. The field itself will leave you behind as you stagnate in your ways.

Instead, you need to get outside your box and learn new techniques. Learning a new language, even only the basics, will let you see your own work in new ways.

Look at yourself one year ago and be embarrassed. You’ve grown since then and learned new ways to solve problems. Be proud of that embarrassment, but don’t be too proud. You’ll be embarrassing yourself a year from now.

Setting Up Spring Batch

Lets do the setup work that we’ll rely on for the rest of the project. We want to configure Spring Batch’s database, configure logging and create a simple reader and writer. All code available on the Google Code project page for GLExtensions.

Spring Batch Database

The database used by Spring Batch is very handy. It allows for restarting jobs after failure, logging of job progress and real time statistics via the Spring Batch Admin Console. There is also the option to use a Map based repository instead which allows for in-memory job runs, but lacks the advantages of the database repository. A third option is to use an in-memory database setup, no advantages from an actual database setup, but it will allow us to switch to a real database easier in the future. This is the setup that Spring Tool Suite gives us when selecting a Spring Batch project template.

First, the repository setup that will configure HQLDB as the Spring Batch repo. The following properties file will configure the database with the Spring Batch schema:

batch.properties

batch.jdbc.driver=org.hsqldb.jdbcDriver
batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true
batch.jdbc.user=sa
batch.jdbc.password=
batch.schema=
batch.schema.script=classpath:/org/springframework/batch/core/schema-hsqldb.sql

The following beans file will set up the job repository, configure component scan and import the jobs configuration which will be discussed later:

beans.xml

 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">

	 <context:property-placeholder location="classpath:batch.properties" />
	 <context:component-scan base-package="com.bitwiseor" />

	 <import resource="classpath:jobs.xml"/>
	
	 <jdbc:initialize-database>
		 <jdbc:script location="${batch.schema.script}" />
	 </jdbc:initialize-database>
 </beans>

The rest of the database setup happens in code:

BatchConfig.groovy

package com.bitwiseor.extensions.config

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
class BatchConfig {
	@Value('${batch.jdbc.driver}')
	private def driverClassName

	@Value('${batch.jdbc.url}')
	private def driverUrl

	@Value('${batch.jdbc.user}')
	private def driverUsername

	@Value('${batch.jdbc.password}')
	private def driverPassword

	@Autowired
	@Qualifier("jobRepository")
	def jobRepository

	@Bean
	def dataSource() {
		BasicDataSource dataSource = new BasicDataSource()
		dataSource.driverClassName = driverClassName
		dataSource.url = driverUrl
		dataSource.username = driverUsername
		dataSource.password = driverPassword
		return dataSource
	}

	@Bean
	def jobLauncher() {
		SimpleJobLauncher jobLauncher = new SimpleJobLauncher()
		jobLauncher.jobRepository = jobRepository
		return jobLauncher
	}

	@Bean
	def transactionManager() {
		return new DataSourceTransactionManager(dataSource())
	}
}

Logging Config

We also want logging to keep track of the work we’ve done. Using SLF4J and Logback is easy enough. All we need is the Logback configuration:

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
 <configuration>
     <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
         <encoder>
             <pattern>%5p %-25logger{25} %m %n </pattern>
         </encoder>
     </appender>

     <logger name="com.bitwiseor">
         <level value="INFO" />
     </logger>

     <logger name="org.springframework">
         <level value="ERROR" />
     </logger>

     <root>
         <level value="INFO" />
         <appender-ref ref="CONSOLE" />
     </root>
 </configuration>

Job Configuration

Now for the interesting piece: job configuration. Spring Batch has an easy pattern that can be followed by most batch applications: read, process, write. The ItemReader interface has a single method to read an item, the ItemProcessor interface converts the read item into a writable item, and the ItemWriter interface simply writes a batch of items out to wherever it needs to. For the sample job to make sure everything is running correctly, we’ll write an ItemReader to read files from a directory, and an ItemWriter that simply writes the item to the info log.

First, the ItemReader. The interface has a single method call that must return an object. Once there are no more objects to read, it returns null. DirectoryItemReader will get an injected directory, then create an iterator once the bean is instantiated. The simple read call will return a file in the directory until there are no files using the iterator. Easy enough.

DirectoryItemReader.groovy

package com.bitwiseor.extensions.batch

import org.springframework.batch.item.ItemReader
import org.springframework.beans.factory.InitializingBean
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component

@Component
class DirectoryItemReader implements ItemReader <File>, InitializingBean {
	@Value('D:/code/glextensions') // hard coded for simplicity
	File directory
	private Iterator <File> iterator;
	
	@Override
	public void afterPropertiesSet() throws Exception {
		assert directory.isDirectory()
		this.iterator = Arrays.asList(directory.listFiles()).iterator()
	}
	
	@Override
	File read() {
		return iterator.hasNext()? iterator.next() : null
	}
}

And then the ItemWriter. The LoggingItemWriter simply logs each item out as an info level log using SLF4J. Groovy makes this stupidly simple.

LoggingItemWriter.groovy

package com.bitwiseor.extensions.batch

import groovy.util.logging.Slf4j

import java.util.List;

import org.springframework.batch.item.ItemWriter
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class LoggingItemWriter implements ItemWriter {
	@Override
	void write(List items) {
		items.each{ log.info it.toString() }
	}
}

Lastly, we need our job XML configuration. The job simply defines the reader and writer and executes in batches of 10.

jobs.xml

<?xml version="1.0" encoding="UTF-8"?>
 <beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/batch"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd">

	 <job-repository id="jobRepository" />
	
	 <job id="sampleJob">
		 <step id="readDir">
			 <tasklet>
				 <chunk reader="directoryItemReader" writer="loggingItemWriter" commit-interval="10"/>
			 </tasklet>
		 </step>
	 </job>
 </beans:beans>

Build File

In order to build and run everything, we’ll use gradle, a Groovy based build tool. The following build file will collect all our dependencies and setup a test script to run the sample job.

build.gradle

apply plugin:'groovy'
apply plugin:'eclipse'
apply plugin:'application'

repositories {
	mavenCentral()
}

def springFrameworkVersion = '3.0.6.RELEASE'
def springBatchVersion = '2.1.7.RELEASE'
def slf4jVersion = '1.6.4'
def logbackVersion = '1.0.3'

mainClassName = 'org.springframework.batch.core.launch.support.CommandLineJobRunner'
run {
    args = ['classpath:/beans.xml', 'sampleJob']
}

dependencies {
	compile gradleApi()
	groovy localGroovy()
	
	compile "org.springframework:spring-jdbc:${springFrameworkVersion}"
	compile "org.springframework:spring-context:${springFrameworkVersion}"
	compile "cglib:cglib-nodep:2.2"
	compile "org.springframework.batch:spring-batch-core:${springBatchVersion}"
	compile "commons-dbcp:commons-dbcp:1.2.2"
	compile "hsqldb:hsqldb:1.8.0.7"	
}

task wrapper(type: Wrapper) {
	gradleVersion = '1.0-rc-3'
}

Running this with gradlew install run will print out the files in our directory as INFO messages as expected. Now we can get to work creating the actual job that will convert extension specifications into wiki documents.

GLExtension Registry

Well, after that sad Heroku mess, I’ve given up temporarily on cloud experiments (although, I will probably look into Cloud Foundry once I get interested again). Instead, lets take a look at Spring Batch in order to make OpenGL developers lives a little easier.

First, some history. OpenGL is a lovely, standardized graphics library implemented on most major operating systems. One of the strength of OpenGL is the large partnership with graphics card manufacturers and the abilty to create extensions. There are literally hundreds of extensions that have been created and defined in the OpenGL Extension Registry.

Manufacturers come up with new ideas, create an extension and implement it in their drivers. If the extensions become especially useful, other vendors adopt the extension and eventually it becomes part of the specification for OpenGL. This is how programmable hardware in graphics cards started in OpenGL and now is required if the card wants to support the latest standards.

So, the registry is useful for developers as it defines the latest features available and how they are implemented, but the specifications are just sitting online in plain text. Often, extensions build upon other extensions, requiring developers to hunt and peck for the information they need.

This lead me to create the OpenGL Extension Wiki on Google Code. The plan was to create a listing of all the extensions in an easily navigable way. Even OpenGL admits that “[r]eading an OpenGL extension is somewhat complicated.” It doesn’t help that they exist as separate text files online. I wanted to create an alternate to the registry that seemed more cohesive, but doing this by hand was painful, so I gave up and the project was left to rot.

Four years later, I was looking through Google’s analytics and noticed that the project is actually getting hits. Approximately ten a day - not overwhelming, but more than I would expect as it isn’t in a very usable state.

This got me thinking, is there any way to automate the creation of wiki docs? There must be. The specification documents are fairly consistent and somewhat well formatted. The trick will be parsing it into a usable form. After pondering this for a little while, I’ve come up with the following design:

Spring Batch is an easy to use framework that takes a lot of the work out of doing repetitive tasks. Specifically, it can be used to drive nearly any repetitive process and inclodes plenty of hooks for logging and error handling.

Once we download all the extension documents, we open each one up, parse it into a usable object representing each of the sections, then write out the document with wiki formatting. Since Google Code stores the wiki in an SVN repo, publishing is as easy as committing the changes.

The difficult part will be parsing and formatting, but that can be dealt with on a step-by-step basis. First, we need to get Spring Batch configured, which is where we’ll go next.

« 2/3 »