I just pushed out a first draft of the Spring integration, available in my spring-prototype branch. For now, I've linked against Spring 3.0. We can easily refactor the code somewhat to support Spring 2.5 as well.

Overview

This integration is interesting because it challenges some the assumptions we make in Arquillian.

The embedded Spring container is straightforward. It's an embedded standalone akin to embedded Weld, OpenEJB and OpenWebBeans. However, Spring is the first programming model we've faced that is not available out of the box in a full embedded or remote container. And it doesn't make sense to create a remote or managed Spring container. Instead, it's implemented as a framework integration that's added to a container, sort of like JSFUnit. We have to extend the programming model of the target container, and thus of the test.

In all, the Spring integration consists of three components:

Spring test enricher (generic)

Spring embedded container

Spring framework integration

Example

Here's an example of a Spring Arquillian test that uses the @Autowired annotation for dependency injection (similar in nature to @Inject).

The location of the Spring configuration file is assumed to be applicationContext.xml in the root of the classpath. You control which configuration file is used by putting the one you want to use into the archive in this location. The default location can be changed using the container configuration.

This test case takes advantage of one of the tricks I've implemented. If you add an empty applicationContext.xml, it will automatically enable the annotation-based wiring. Alternatively, you can:

The annotated auto-wiring support is only working in the embedded Spring container at the moment.

Class loading

I've spent some more time thinking about the class loading issues in an embedded container. Spring is pretty flexible with regards to plugging in a custom classloader. That's a good fit for the ShrinkWrap class loader. However, there is still an issue.

While the ShrinkWrap class loader makes assets in the ShrinkWrap archive available on the classpath, it does not enforce that only classes and resources in the archive should be visible to the test runner. This is important. Otherwise, the embedded containers could give false results by allowing external classes and resources to be visible, those which won't be there once the archive is deployed remotely.

At first, the instinct is to pass in a null parent class loader. But this is asking for trouble since some classes will already be loaded by the time the test is run and you'll run into linkage errors. A better approach is to refuse to load a class which is not present in the archive, but then allow delegation through the normal chain. Here's how it looks in pseudo-code:

We never want to use the parent class loader for loading resources, so I just added:

@Override
public URL getResource(String name)
{
// bypass parent; the only place we should look for resources is in the archives
return findResource(name);
}

I also found a problem with the way the ShrinkWrap class loader was caching input streams. When Spring attempted to use the input stream, it was marked as closed (can't get to the bottom of why).

Packaging Spring

When using Spring in a remote container, it's necessary to package Spring in the test archive so that it's available to the deployed application. This is where we are challenging some past assumptions because we've never really dealt with an "add-on" programming model (except for test integrations like JSFUnit).

The hard part is knowing what to package. One approach is to simply package all the Spring classes available on the test classpath using an auxiliary archive appender:

However, I ran into a major limitation with this API method. ShrinkWrap attempts to load each class as it's being added to the archive. If one of those classes implements or extends a class/interface that is not available, the operation blows up. What we really want is to just transfer the bytes of the class into the archive (no need to load it). I implemented a little hack using an ArchiveFilter to add the classes as resources. But we should think more about this case.

Another approach is to have the user designate a location where all the Spring dependencies reside and merge all those libraries together into a spring-all.jar (or have them just package a spring-all.jar library).

We want to minimize the burden on the developer without painting them into a corner. So I think we need to brainstorm the best way to ship a framework to the container.

Looking for feedback

This prototype is a start. Now that you have something to play with, perhaps it will begin to move along quickly as a complete replacement for Spring's testing framework (the point being to align w/ the portability and in-container options that Arquillian provides).

Ah, I remembered something else I meant to include in the class loading section. Spring allows you to set the classloader it uses to resolve bean classes and resources. So it's pretty straightforward to pass in the ShrinkWrap classloader to Spring in the standalone embedded container (using more strict version, though).

Where we get bitten is embedded deployable containers (Embedded GlassFish, Embedded Jetty, etc).

In the embedded deployable containers, we hand the deployment off to the container and expect it to behave just like a remote container. However, the difference is that the container was started in the same JVM and hence under the test classpath. As a result, the application can see classes that are outside of the test archive.

Fixing this is a complicated issue (in my initial thoughts about it). The container needs to have access to the whole test classpath, but the test archive running inside of it should not (it should only see the ShrinkWrap archive).

If we try to modify how the programming model is initialized upon deployment of the archive, (such as controlling which classes and resources Weld or Spring use), we'll, we can't. We don't have access to the test archive object at that point and therefore wouldn't know what classes and resources to which to restrict the classpath.

Another approach is to use the native features of the container to set the classloader. However, I tried this with Jetty and all of a sudden lots of things can't be found. It's a lot of spinning wheels in mud to find out what is going to work.

We may just want to chase this down in another thread. The way I see it, if Arquillian is going through the effort of using a ShrinkWrap archive, it shouldn't allow applications to load classes and resources outside of that archive. This is happening in embedded containers. We may just need to go with a higher level validator, such as ensuring that Spring does not access bean classes that aren't in the ShrinkWrap archive.

However, I ran into a major limitation with this API method. ShrinkWrap attempts to load each class as it's being added to the archive. If one of those classes implements or extends a class/interface that is not available, the operation blows up. What we really want is to just transfer the bytes of the class into the archive (no need to load it). I implemented a little hack using an ArchiveFilter to add the classes as resources. But we should think more about this case.

This isn't an ideal solution, I admit. My hope is that it communicates the requirement and spawns some other ideas.

* There are a load of filters that can be applied to which dependencies are copied. You can also switch to the copy task, which will copy individual JAR files. This task makes full use of dependency management.