tags:

views:

1365

answers:

2

Here's my variation on the "multiple artifacts from Maven build" question:

I'm porting from Ant to Maven. My application is an EJB server that is packaged as an EAR, but it also exposes a client JAR for use by other client apps. This jar contains the EJB interfaces, facade class and some helpers.

I know that the Maven way is to have one artifact per project (POM); however, both artifacts (server EAR and client JAR) need to be built from the same source tree - server and client share, for example, the EJB and 'home' interfaces.

How do I do this in Maven?

Do I have one project containing two POMs, say server-pom.xml & client-pom.xml? I was thinking I could also have a parent POM (pom.xml) that can be used to build both client and server with one foul swoop? However, the lifecycles diverge after the 'package' phase, since the server has to go through assembly (tar/gzip), while the client is done after 'package' and can simply be installed into the repository.

Any advice/experience on the best way to approach this?

+1  A: 

I've used multimodule projects to solve this before.

project/
    pom.xml  <- type=pom, lists sub modules
    ejb/
        src/main/java, etc.
        pom.xml <- type=ejb, describes ejb module, has dependency on "jar" module
    jar/ 
        src/main/java, etc.
        pom.xml <- type=jar, simple, builds jar
    ear/ 
        pom.xml <- type=ear, has reference to ejb module that it should use
    ...

I've use this approach for rather complex projects that may have a dozen different modules that must be built together. See the ear docs for referencing the ejb.

The parent pom.xml uses the modules tag:

<modules>
    <module>jar</module>
    <module>ejb</module>
    <module>ear</module>
</modules>

And the child pom.xml's use the parent tag:

<parent>
    <groupid>mygroup</group>
    <artifactId>parentName</artifactId>
</parent>
John Paulett
@John, I agree on the multi-module approach. I'm not quite sure I understand the use of the EAR packaging for the *client* though: the *client* jar should not be inside the EAR, and also it needs to be built from the same codebase (src/main/java) as the EAR code, since both artifacts share the EJB interface/home classes.
Cornel Masson
+3  A: 

I know that the Maven way is to have one artifact per project (POM); however, both artifacts (server EAR and client JAR) need to be built from the same source tree - server and client share, for example, the EJB and 'home' interfaces.

There are some exception to the "one artifact per project" rule, EJB projects being one of them. So, the maven-ejb-plugin can be configured to generate the EJB jar and a client JAR like this:

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-ejb-plugin</artifactId>
        <configuration>
           <generateClient>true</generateClient>
        </configuration>
      </plugin>
    </plugins>
  </build>

To use the ejb client in another project, just add it as dependency of <type>ejb-client</type>:

<project>
  [...]
  <dependencies>
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>ejb-project</artifactId>
      <version>1.0-SNAPSHOT</version>
      <type>ejb-client</type>
    </dependency>
  </dependencies>
  [...]
</project>

See Generating an EJB client, Using the ejb-client as a dependency and the documentation of the ejb mojo for more details (including how to customize the classes included/excluded from the ejb client).

Pascal Thivent
Excellent! Let me try that.
Cornel Masson
@Pascal, this works fine except when the EJB client is a dependency of another module within the same multi-module project. Firstly, you can't use the EJB client as a nested module in the parents list of modules, since it hasn't got its own folder - it's just a by-product of the EJB module. Secondly, the EJB client artifact is only created in the package phase, so the reactor wouldn't be able to add it to the combined classpath anyway.
Cornel Masson
So it seems I have to abandon this approach and create an explicit module for the EJB client. However, this means this new 'ejb-client' module would have to share the Java source with the main 'ejb' module (EJB interfaces are shared). I assume I'll keep the source in the main 'ejb' module, and configure the 'ejb-client' module to point to the same source folder (e.g. ../ejb/src)?
Cornel Masson
@Cornel Hmm... what? 1. You're not supposed to declare the ejb-client as a module 2. An ejb-client dependency is taken into account during a rector build to calculate the build order 3. The classpath is built using the local repository so you have to `install` your ejb-client, like any other artifact. In other words, I don't understand what you're saying :)
Pascal Thivent
I hoped there was a way of compiling a complex multi-module project - where some nested modules depend on others (peers) - in *one pass*, i.e. without first manually compiling and installing those modules that are dependencies.
Cornel Masson
@Cornel You always have to run `mvn install` for a multi-modules project, dependencies between modules are resolved through the local repository.
Pascal Thivent
Ah, I get it now. I was trying to replicate our Ant-based process where the build engineer would do a new build by running a "package" first, then check the output, and then only run the full assemble/deploy. I was thinking that the first step would be to run 'mvn package' (to verify everything that's produced) and then 'mvn install' - in this case I have the problem as described above.I see now the user would just have to run 'mvn install' from the start. Thanks @Pascal.
Cornel Masson