Using a checked list in Java

Java offers checked versions of the collection classes List,  Set and Map. Both normal and checked collections provide type checking, but checked collections just make it easier to spot a type-error when it occurs. Let’s see the difference.

A checked list can be obtained from a factory method exposed by the Collections class.

public static <E> List<E> checkedList(List<E> list,
                                      Class<E> type)

The method expects a list object and a type token and returns the checked list. The type token should be the same class as the parameter type declared by the list.

According to the documentation, a checked list is a “dynamically typeset view over the specified list”.  We also learn that:

To illustrate the point, let’s define a method that can add “pears” to “apples”. We can pretend the method is from a third-party library written before the introduction of generics (in JDK5).

@SuppressWarnings("unchecked")
void unsafeAdd(Collection c, Object e) {
    c.add(e);
}

The author used a raw type java.util.Collection and annotated the method with @SuppressWarning to silence the compiler.

For the sake of the argument, let’s also examine a type-safe version of the same method.

// add element to collection in a generic fashion
<T> void add(Collection<T> c, T e) {
    c.add(e);
}

If we try to call the add() method in a way that violates type guarantees, our code doesn’t even compile. On the other hand, with the unsafe method we get no such warning.

Now let’s see what happens when we attempt to pass an normal/unchecked list to the unsafeAdd method.

List<Integer> list = Arrays.asList(1, 2, 3);
unsafeAdd(list, "4");

Here’s the stack trace:

 Exception java.lang.UnsupportedOperationException
        at AbstractList.add (AbstractList.java:153)

The UnsupportedOperationException is a generic error thrown by the parent class which that doesn’t really point to the actual problem. Had we performed many different operations on the list (i.e. in the same method scope), it would have been unclear which one caused the error.

Now let’s pass a checked list to the unsafeAdd method.

List<Integer> ckList =  Collections.checkedList(list, Integer.class);
unsafeAdd(ckList, "5");

Now we get another error – a better one. Here’s the stack trace:

Exception java.lang.ClassCastException: Attempt to insert class java.lang.String element into collection with element type class java.lang.Integer
|        at Collections$CheckedCollection.typeCheck (Collections.java:3047)

The ClassCastException correctly identifies the problem and has a nice, helpful message.

Conclusion

While both checked and unchecked lists prevent type violations, checked list operations fail immediately, with a clear, appropriate exception that is useful for debugging.

How to deploy a Spring Boot app to WildFly

According to the spring.io docs, stand-alone Spring Boot applications can be packaged as a WAR or JAR, which can be deployed to any Servlet 3.1+ compatible container.

You might think this is possible out of the box, but in reality we’ll need a couple of tweaks. Nothing too complicated.

Let’s use the example REST service application provided by the Spring guides.  As a quick reminder, all this app does is respond to HTTP Get requests with a greeting in JSON format.

To deploy the app to an external server, first we extend SpringBootServletInitializer; this is the only change in the application code we are going to need.

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Next we edit the gradle.build file.

Remember that Spring Boot apps run in the embedded Tomcat Server. Since our target HTTP runtime (WildFly) uses the Undertow servlet, we don’t want any Tomcat stuff on the classpath.

Let’s exclude the spring-boot-starter-tomcat module from the parent project.

gradle.build

configurations {
    all.collect { configuration ->
        configuration.exclude   group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
    }
}

Next we declare a dependency to javax.servlet module.

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("javax.servlet:javax.servlet-api:3.1.0")
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

Finally let’s add the WAR plugin.

plugins {
    id 'war'
}

Now try to package the app using ./gradlew bootWar. If this succeeds, you should be able to deploy the file to WildFly without errors.

Note that WildFly will use the application name as the context root. Visit http://localhost:8080/gs-rest-service/greeting to test the result.

{"id":1,"content":"Hello, World!"}

Hope it helps.