AppFuse 2.0 Status
Last week I managed to get AppFuse to compile with Maven 2, this week I got all the tests passing. As noted last week, one of the most frustrating things I found with Maven 2 is that WAR dependencies can't have .java/.class files that other projects can depend on. Because of this, I had to split each web/framework project into two: one that's packaged as a JAR and one that's packaged as a WAR.
I had a hard time deciding between the following directory structures:
1.
web - jsf - war
2.
web - jsf war - jsf
3.
web - jsf - jsf-war
I ended up using the 3rd option because it seemed like the most intuitive for AppFuse developers.
It's pretty ugly IMO, but it's also The Maven Way. To properly use convention-over-configuration, it's best not to go against the grain. The current directory structure can be seen by clicking here.
In an ideal world, I'd only need to do a single WAR project for each framework, and any classes compiled within the WAR would be available to child projects. However, WARs seem to be a strange beast in Maven. Not only can you not use a WAR's classes, but it doesn't seem like the dependency/exclusion mechanism works for WARs. This means that a WAR's dependencies have to be rather dumb, and they should only contain static files. Of course, AppFuse 2.0 could operate like it does now - where your project starts out with a bunch of files from the get-go. However, the problem with this setup is it makes it difficult to upgrade.
The good news is using this new setup, you can create a project and use (and upgrade!) AppFuse pretty easily.
Below are the current steps (subject to change as we get closer to a release), assuming you already have Maven 2 and MySQL installed.
1. Create a directory and create a pom.xml file in it. The groupId, artifactId and version can be any values you choose. In pom.xml, add the following:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany</groupId> <artifactId>test-appfuse</artifactId> <packaging>war</packaging> <version>1.0</version> <build> <defaultGoal>package</defaultGoal> <plugins> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty6-plugin</artifactId> <configuration> <scanIntervalSeconds>3</scanIntervalSeconds> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>appfuse</id> <url>http://static.appfuse.org/repository</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.appfuse</groupId> <artifactId>appfuse-struts-war</artifactId> <version>2.0-SNAPSHOT</version> <type>war</type> </dependency> </dependencies> </project>
2. Download appfuse.sql and run the following commands.
mysqladmin -u root -p create appfuse mysql -u root -p appfuse < appfuse.sql
If you already have an appfuse database, you'll need to drop that first. Note: the database name and type is hardcoded now, but you'll be able to change that when 2.0 is released, just like you can with AppFuse 1.x.
3. Run "mvn jetty6:run-war" and grab a beer (it's the weekend right?!). After everything has downloaded and started, login to your app with mraible/tomcat at http://localhost:8080/artifactId. If you get errors about missing dependencies, run the command again. If you get an error about web.xml from Jetty, run "mvn -U jetty6:run-war".
I've run into plenty of issues during this migration. I've listed them below and will likely add to this list over the next few days.
- There is a DbUnit plugin for Maven 2, but I was only able to get it to work by checking it out from Subversion and building/installing it locally. Maybe I just have the wrong plugin repository, as it seems to be available here. Thanks to Brian Topping for writing this plugin - it works great!
- The DbUnit plugin requires a sample-data.xml file for each project, as well as a fair amount of XML for its configuration. It would be nice to somehow use one sample-data.xml file and variables to configure. Maven's properties feature will probably work for configuring the variables.
- The DbUnit plugin and Hibernate3 plugin are configured to run at the "compile-test" phase. Running "mvn -Dmaven.test.skip=true" still runs both plugins. Ideally, they would be skipped when this flag is on.
- The "service" project has a hard-coded dependency on appfuse-hibernate. I need to figure out a way to have this project be dao-layer agnostic. This will likely require that tests don't load up applicationContext-hibernate.xml, or there's a test for each DAO option.
- Any tests (in service and web) that talk to a database load up "classpath*:/applicationContext-hibernate.xml". Ideally, I could load up "classpath*:/applicationContext-*.xml", but this doesn't work when loading context files from JARs. Therefore, the context file packaged in JARs is likely going to have to be generic, possibly named applicationContext-dao.xml.
- Many of the tests in web/JAR projects depend on i18n properties files, or XML files in WEB-INF (i.e. faces-config.xml and action-servlet.xml for Spring/WebWork). To make them pass, I had to copy files from the WAR projects into the JAR projects' src/test/resources directory. Obviously, duplicate files between projects is a bad thing, so hopefully tests can be re-worked to eliminate duplication.
- The maven-jetty6-plugin for Maven 2 works great, the (newer) maven-jetty-plugin does not.
- Because WARs are overlaid, "mvn jetty6:run" cannot be used w/o running "mvn war:inplace" first. Using "mvn jetty6:run-war" or "jetty6:run-exploded" works, but doesn't auto-refresh files when you edit/save them in your IDE. Possible workarounds include:
- Modifying the Jetty plugin to support overlaid WARs
- Configuring IDEs to auto-deploy files into the target/WAR directory upon save
- Running "mvn war:inplace" first
- Since scope=provided and scope=test are not added to the classpath for child projects, there's a lot of dependency duplication between web/*/pom.xml files. I doubt there's a workaround for this.
- I still need to hook Cargo in and get Canoo WebTests running.
- Tapestry only allows global-i18n files to be loaded from WEB-INF, therefore, I have to have duplicate i18n files in WEB-INF and src/main/resources. The reason is some service classes expect i18n files to be in the classpath. This is a Tapestry issue and not a Maven one.
- I need to figure out a way to have the logj4.xml in the final WAR be the one used, or make sure it isn't included at the root of any appfuse-* JARs.
If you're interested in seeing AppFuse 2.0 from a developer's perspective, you can download it in its current form from:
http://static.appfuse.org/downloads/appfuse2-20060826.zip
Please keep in mind that this is still very much in the "proof of concept" phase, so any advice is most welcome. CollabNet has scheduled our CVS to SVN migration for Monday, so hopefully I can checkin everything after that.
And the invocation should be:
mvn jetty:run
or
mvn jetty:run-war
Posted by dahernan on August 27, 2006 at 10:09 AM MDT #
"The maven-jetty6-plugin for Maven 2 works great, the (newer) maven-jetty-plugin does not."
Yesterday I test maven-jetty-plugin with equinox and works great :S
Posted by dahernan on August 27, 2006 at 10:19 AM MDT #
Posted by lane.ma on August 28, 2006 at 06:15 AM MDT #
Posted by Matt Raible on August 28, 2006 at 04:30 PM MDT #
Posted by Eko SW on August 28, 2006 at 06:28 PM MDT #
- The distribution is damn small now :).
When do you plan to apply this on the main trunk?
At least to get rid of the huge /lib :) ?
Thanks in advance,
Ahmed.
Posted by Ahmed Mohombe on August 29, 2006 at 02:44 PM MDT #
Posted by Tim O'Brien on September 28, 2008 at 03:10 PM MDT #