Setting up a JavaEE project with IntelliJ and maven

2011/03/03

Setting up a Java EE project with IntelliJ is a trivial task. Getting a multi-module setup to work properly with quick deployments and maven builds is not at all a trivial task.

My requirements:

  • A multi-module project with at least a web module, an ejb module and a common module with shared code to be used by ejb/web
  • Maven building ear file.
  • Easy deployment to local app server (Glassfish and Websphere Application Server)
  • Quick compiles and hot swapping of recompiled code into running app.
  • EJB 3.0 (not 3.1)

Download attached  example project and have a look for what it’s worth.

This is how I created my project using IntelliJ IDEA 10 Ultimate edition:

1. Create modules:

  • Create EJB module named ‘ejb’ and add EJB facet. Don’t “Fix” javaee.jar yet!
  • Create Web module named ‘web’ with Web facet. Don’t create any artifact yet. Move and rename web folder within web module! Give it name ‘webapp’ and place it under src/main.
  • Create EAR module named ‘ear’ with JavaEEApplication facet. Don’t create any artifact yet.
  • Create common module named ‘domain’ (or whatever it’s going to contain)

2. Edit root pom.xml and module pom files.

Root pom.xml should look something like:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>dag</groupId>
    <artifactId>mavenear</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>
    <modules>
        <module>domain</module>
        <module>ejb</module>
        <module>web</module>
        <module>ear</module>
    </modules>

    <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <name>Java.net Repository for Maven</name>
            <url>http://download.java.net/maven/2/</url>
            <layout>default</layout>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>javaee</groupId>
            <artifactId>javaee-api</artifactId>
            <version>5</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

Domain module pom.xml should look like:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mavenear</artifactId>
        <groupId>dag</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>dag.mavenear2</groupId>
    <artifactId>domain</artifactId>
    <packaging>jar</packaging>

</project>

EJB pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mavenear</artifactId>
        <groupId>dag</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>dag.mavenear2</groupId>
    <artifactId>ejb</artifactId>
    <packaging>ejb</packaging>

    <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <name>Java.net Repository for Maven</name>
            <url>http://download.java.net/maven/2/</url>
            <layout>default</layout>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>dag.mavenear2</groupId>
            <artifactId>domain</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-ejb-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <ejbVersion>3.0</ejbVersion>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Web pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mavenear2</artifactId>
        <groupId>dag</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>dag.mavenear2</groupId>
    <artifactId>web</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>dag.mavenear</groupId>
            <artifactId>domain</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>dag.mavenear2</groupId>
            <artifactId>ejb</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>web</finalName>
    </build>
</project>

EAR pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mavenear</artifactId>
        <groupId>dag</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>dag.mavenear2</groupId>
    <artifactId>ear</artifactId>
    <packaging>ear</packaging>
    <dependencies>
        <dependency>
            <groupId>dag.mavenear</groupId>
            <artifactId>ejb</artifactId>
            <version>1.0</version>
            <type>ejb</type>
        </dependency>
        <dependency>
            <groupId>dag.mavenear</groupId>
            <artifactId>web</artifactId>
            <version>1.0</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>dag.mavenear</groupId>
            <artifactId>domain</artifactId>
            <version>1.0</version>
            <type>jar</type>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-ear-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <modules>
                        <webModule>
                            <groupId>dag.mavenear</groupId>
                            <artifactId>web</artifactId>
                        </webModule>
                        <ejbModule>
                            <groupId>dag.mavenear</groupId>
                            <artifactId>ejb</artifactId>
                        </ejbModule>
                        <jarModule>
                            <groupId>dag.mavenear</groupId>
                            <artifactId>domain</artifactId>
                        </jarModule>
                    </modules>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3. Now write some code:

Common module: Create a class as follows:

public class Common {
    public String sayHey() {
        return "Common says hey!";
    }
}

For EJB module: Let IntelliJ create the EJB stub by adding a stateless session bean to main src. Name it ‘Dag’ and add the following method to the bean:

@Stateless(name = "DagEJB")
public class DagBean implements Dag {
    public DagBean() {
    }

    @Override
    public String sayHey() {
        Common common = new Common();
        return "EJB says hey. Via EJB: " + common.sayHey();
    }

}

Extract an interface named ‘Dag’ with the single method sayHey().

For Web module:

Let IntelliJ IDEA create a servlet stub and name it ‘DagServlet’. IntelliJ edits web.xml for you but you need to add a servlet mapping. This can be done using a IntelliJ feature or add the following to web.xml:

<servlet-mapping>
    <servlet-name>DagServlet</servlet-name>
    <url-pattern>/servlet/*</url-pattern>
</servlet-mapping>

Add the following code to the servlet:

@EJB(name = "DagEJB")
private Dag ejb;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ServletOutputStream out = response.getOutputStream();
    out.println("<html><body>");
    out.println("<p>Servlet says hey.</p>");
    Common common = new Common();
    out.println("<p>" + common.sayHey() + "</p>");
    out.println("<p>" + ejb.sayHey() + "</p>");
    out.println("</body></html>");
    out.flush();
}

4. Build with maven

Now it should be able to build an ear file using Maven, assuming Maven is setup correctly, Run the 'package' goal in the root pom.xml and check the created ear-1.0.ear in folder .../ear/target
Deploy the ear file on Glassfish and go to http://localhost:8080/web/servlet

5. Deploy from IntelliJ

Now that we know the app works as wanted it’s time to setup deployment from within IntelliJ IDEA.

Chances are good that IntelliJ at this point has created a number of artifacts for you. Setup a Glassfish configuration and select artifact ‘ear:ear exploded’ for deployment.

Edit application.xml in ear module and set web context root to /web:

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd"
             version="5">

    <module>
        <ejb>ejb-1.0.jar</ejb>
    </module>
    <module>
        <web>
            <web-uri>web.war</web-uri>
            <context-root>/web</context-root>
        </web>
    </module>
</application>

Perhaps you will need to remove artifact output folder …/ear/target/ear-1.0 if maven has built to this folder before.

Start the Glassfish server in debug mode and go to http://localhost:8080/web/servlet You will most likely end up with a ClassNotFoundException. The problem is missing MANIFEST.MF files in the web and ejb module. These can be added from the artifact view in IntelliJ:

1. Navigate to artifact “ear:ear exploded” and select ejb-1.0.jar. There should be a “Create Manifest…”-button below. Click it and select the ejb module root.

2. Insert “domain-1.0.jar” to the Class-Path field.

3. Repeat for the web:war artifact. The META-INF should be located in the webapp folder next to WEB-INF.

Potholes along the road:

  • At some point I had a web.xml without servlet API version (2.5). This lead to Glassfish not being able to detect my annotated EJB:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
...
</web-app>

Resources:

Maven EAR plugin: http://agoncal.wordpress.com/2009/10/23/because-i-always-forget-how-to-use-maven-ear-plugin/

Classloader and EAR structure: http://www.technicalfacilitation.com/get.php?link=classloading

Advertisements

9 Responses to “Setting up a JavaEE project with IntelliJ and maven”

  1. Kedar Mhaswade said

    Thank you! I wish someone (possibly me) creates all of this as a sensible template for the Maven Archetype. Why does Maven Archetype generation have to be so empty?

  2. I am new in intellij idea and don’t understand how to do.
    Can you give some step by step configuration in intellij?

  3. Good write-up.

    I can compile the domain and ear Maven projects, but the ejb, web, and mavenear2 projects fail due to missing dependencies. For example, when I try to compile the ear Maven project, I get the following error:

    [ERROR] Failed to execute goal on project ear: Could not resolve dependencies for project dag.mavenear2:ear:ear:1.0: Failed to collect dependencies for [dag.mavenear2:ejb:jar:1.0 (compile), dag.mavenear2:web:war:1.0 (compile), dag.mavenear2:domain:jar:1.0 (compile), javaee:javaee-api:jar:5 (provided)]: Failed to read artifact descriptor for dag.mavenear2:domain:jar:1.0: Could not find artifact dag:mavenear2:pom:1.0 in maven2-repository.dev.java.net (http://download.java.net/maven/2/) -> [Help 1]

    Do you know how to fix that?

    Thanks in advance for any help.
    –Ian

  4. BTW, I’m using IntelliJ 10.5.4.

  5. JL Cetina said

    Its possible to use intellij idea with tomee and deploy the ear to tomee using the IDE?

  6. As the admin of this website is working, no question very quickly it will be renowned, due
    to its quality contents.

  7. Cost me some hours, but in latest IDEA, you also had to add something to the pom.xml, to make it available in application.xml:
    true

    Source:
    http://stackoverflow.com/questions/12093346/maven-ear-plugin-is-not-including-jarmodule-into-application-xml/17322692#17322692

  8. And thanks a lot for the article of course! I’m super gald I managed to get the ear deployments in IDEA right. 🙂

  9. Béla Boda said

    The application.xml problem should be pointed out. If you start from scratch with maven projects of the IDE and do the project configuration with maven then NO application.xml is going to be available. Because it does not come with the maven ear archetype since the maven generates it automaticlly into the target. Sothat the Intellj will not be able to deploy the application.
    Editing the application.xml manually – mentioned above – is not an optional step (in orther to specify the web context root), but a mandatory one to make Intellij able to deploy at all.
    It also mentioned that you might need to build with maven first, but with a running appserver it is usually not possible, because the locked runtime resources.
    So you lose the automatic maven generation of application.xml and you need to maintain it manually if your project strucutre changes.
    Great article, thanks for your efforts.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: