javamavenapache-tomeetomee-7

TomEE embedded/bundle with single application


I'm currently exploring building a webapp which is bound to a TomEE server, i.e. it either runs as the sole (additional) application within a TomEE or has a TomEE embedded. The webapp should support servlets and JSPs (basically we'd like to use Struts2 here).

I tried a couple of options which all had several problems:

Option 1: Use the Maven-Shade-Plugin to bundle our application and tomee-embedded as described here: http://www.tomitribe.com/blog/2015/03/50-shades-of-tomee/

Problems:

1) Deploying the bundled application only works with the 7.x version via the --as-war option but 7.x is not yet production ready and I couldn't find any planned release dates

2) Deploying JAXRS webservices worked fine but servlets didn't e.g. web.xml was not picked up, there were classloading issues etc.

Option 2: Use the TomEE-Maven-Plugin to build a zip which contains tomee as well as our webapp.

Problems:

1) This only works with plugin version 7.x (not production ready yet), but this should not affect the resulting build (which is based on TomEE 1.7.2)

2) There are library conflicts since TomEE might load some libs that are also provided by the application (in a different version).

Since the application is tied to the server and vice versa it should be ok to place all libraries into to TomEE's lib folder which works by pointing the Maven plugin to the folder the dependencies are downloaded to. This works but there's no conflict resolution, i.e. the libs are just copied to the target folder.

I now could manually configure the plugin to remove each conflicting artifact but as the application and thus the number of potentially conflicting libraries grows this can become quite cumbersome.

Option 3: Like option 2 but without using the TomEE-Maven-Plugin.

I guess it should be possible to use a pre-packaged TomEE and some Maven plugins to build a distributable zip which would solve problem no. 1 but problem no. 2 - which is the bigger one - still remains: handling all the dependencies manually would be cumbersome.


So finally, the question is: how should I build that application?

I'd prefer option 1 but I couldn't yet find any documentation/example/tutorial on how to make that work.

Options 2 and 3 would be ok as well but I'd need some way to have Maven resolve the dependency conflicts without requiring me to check each dependency, especially the transitive ones.


Solution

  • This MySQL database application is similar to the one you describe using Maven to handle dependencies and and loading tomee embedded on start up.

    public class Main {
        public static void main(String[] args) {
                String currentDir = null;
            try {
                currentDir = new File(".").getCanonicalPath();
                LOG.info("Current application directory: " + currentDir);
            } catch (IOException e) {
                e.printStackTrace();
            }
            String war = currentDir + "\\target\\app.war";
            String tomeeConf = currentDir + "\\src\\main\\tomee\\conf\\tomee.xml";
    
            // ARGUMENTS
            String[] setup = { "--path", war, "--tomeexml", tomeeConf };
            try {
                // PROGRAM:
                org.apache.tomee.embedded.Main.main(setup);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    Tomee embedded documentation is a little sparse as this is still a technology under development, and useful working examples are hard to come by. Configuration is challenging, and I would be remiss in failing to credit Mark Struberg for his assistance in getting my own program up and running.

    Tomee embedded requires a configuration file located at: src/main/tomee/tomee.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <tomee>
    <!-- Connection details specified in src/main/resource/persistence.xml -->
    <Resource id="APP-DATA" type="javax.sql.DataSource">
            IgnoreDefaultValues true 
            JdbcDriver com.mysql.jdbc.Driver
            JdbcUrl jdbc:mysql://localhost:3306/scc-data
            UserName admin
            Password pass
            JtaManaged true
        </Resource>
    </tomee>
    

    Tomee provide these key defaults:

    However, without any arguments, the apache-tomee directory is set to "tomee-embedded_" + System.currentTimeMillis() + "-home"] on each run and the app name and path are set to null, yielding a 404 error at localhost:8080. For consistency, set the directory to a single location.

    Also, Tomee defaults to OpenJPA as JPA Provider. When I began development, Tomee 7.0.2 utilized OpenJPA 2.4.1, which had a problematic unresolved bug [OPENJPA-2635 3/20/16] prompting a switch to Hibernate. (This bug may well have been fixed with version 7.0.3.) Tomee's load process apparently implements OpenJPA as provider before it reads either persistence.xml or tomee.conf. To prevent premature loading of OpenJPA, add to persistence.xml:

    http://stackoverflow.com/questions/40818396/

    Hibernate should also be added to the apache-tomee library, in my case hibernate-core.jar, located at F:\theapp\apache-tomee\lib with its scope set to provided in the pom.

    http://stackoverflow.com/questions/10852035/how-to-use-tomee-with-hibernate

    Note that Tomee 7.0.3 implements Tomcat 8.5.11 and JavaEE 7 and utilizes: Java Servlet 3.1, JSF 2.2 (myfaces 2.2.11), JSTL 1.2, JSP 2.3,
    EL 3.0, and JPA 2.1.

    Parent pom:

    <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>rfpeake</groupId>
      <artifactId>theapp-parent</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>pom</packaging>
      <name>theapp</name>
      <description>Database project</description>
    
        <modules>
            <module>../theapp-app</module>
        </modules>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <tomee.version>7.0.3</tomee.version>
            <tomee.classifier>webprofile</tomee.classifier>
            <mysql-connector-version>5.1.40</mysql-connector-version>
            <hibernate.version>5.2.9.Final</hibernate.version>
            <primefaces.version>6.1</primefaces.version>                    
        </properties>
    
        <dependencies>
    
            <!-- TOMEE EMBEDDED -->
            <dependency>
                <groupId>org.apache.tomee</groupId>
                <artifactId>tomee-embedded</artifactId>
                <version>${tomee.version}</version>            
            </dependency>       
    
            <!-- Declaration for JSF 2.2 with Tomee 7 -->
            <dependency>
                <groupId>javax</groupId>
                <artifactId>javaee-web-api</artifactId>
                <version>7.0</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- JPA spec [required] -->
            <dependency>
                <groupId>org.apache.geronimo.specs</groupId>
                <artifactId>geronimo-jpa_2.0_spec</artifactId>
                <version>1.1</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- Hibernate -->
            <dependency>
             <groupId>org.hibernate</groupId>
                <artifactId>hibernate-core</artifactId>
                <version>${hibernate.version}</version>
                <scope>provided</scope>
            </dependency>
    
            <!--  Hibernate EHCache -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
    
            <!-- Primefaces -->
        <dependency>
                    <groupId>org.primefaces</groupId>
                    <artifactId>primefaces</artifactId>
                    <version>${primefaces.version}</version>
    
            <!-- JUnit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    </project>
    

    theapp-app pom:

    <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>
    
        <parent>
            <groupId>rfpeake</groupId>
            <artifactId>theapp-parent</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <relativePath>../theapp-parent/pom.xml</relativePath>
        </parent>
    
        <artifactId>theapp-app</artifactId>
        <packaging>war</packaging>
        <name>Database Application</name>
    
        <build>
            <finalName>theapp</finalName>
    
            <!-- pluginManagement tag required in Eclipse to avoid error:
                Plugin execution not covered by lifecycle configuration. -->
            <pluginManagement>
                <plugins>               
                    <plugin>
                        <groupId>org.apache.tomee.maven</groupId>
                        <artifactId>tomee-embedded-maven-plugin</artifactId>
                        <version>${tomee.version}</version>
                        <configuration>
                            <tomeeVersion>${tomee.version}</tomeeVersion>
                            <tomeeClassifier>
                                ${tomee.classifier}</tomeeClassifier>
                            <synchronization>
                                <extensions>
                                    <!-- To update each time app built
                                        with mvn compile -->
                                    <extension>.class</extension>
                                    <extension>.properties</extension>
    
                                    <extension>.css</extension>
                                </extensions>
                                <updateInterval>2</updateInterval>
                            </synchronization>
                            <!-- For some reason, must to be false
                                or reloading does not work! -->
    
                            <reloadOnUpdate>false</reloadOnUpdate>
    
                            <warFile>
                                ${project.build.directory}/${project.build.finalName}
                            </warFile>
                            <!-- path tused by tomEE in the tomee:deploy and 
                                tomee:undeploy goals -->
                            <path>
                                ${project.build.directory}/apache-                              tomee/webapps${project.build.finalName}
                            </path>
                            <args>
                                -Djava.awt.headless=true -Dfile.encoding=UTF-8 -server
                                -Xms128m -Xmx4096m -XX:PermSize=196m -XX:MaxPermSize=128m
                                -XX:+DisableExplicitGC</args>
                            <libs>
                                <lib>
                                    mysql:mysql-connector-java:${mysql-connector-version}
                                </lib>
                            </libs>
                        </configuration>
                        <dependencies>
                            <dependency>
                                <groupId>org.apache.tomee</groupId>
                                <artifactId>apache-tomee</artifactId>
                                <version>${tomee.version}</version>
                                <classifier>${tomee.classifier}</classifier>
                                <type>zip</type>
                                <!-- set this to runtime for it to work -->
                                <scope>runtime</scope>
                            </dependency>
                        </dependencies>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <version>2.3.2</version>
                        <configuration>
                            <source>1.7</source>
                            <target>1.7</target>
                            <showDeprecation>true</showDeprecation>
                        </configuration>
                    </plugin>
                </plugins>
            </pluginManagement>
        </build>
    </project>
    

    Regarding the plugin management tag, see http://stackoverflow.com/questions/6352208/how-to-solve-plugin-execution-not-covered-by-lifecycle-configuration-for-sprin

    This may not answer all your configuration questions or provide the ideal framework for your porject, but it should get you started.