<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="myPU"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <properties> <property name="javax.persistence.schema-generation.database.action" value="${javax.persistence.schema-generation.database.action}"/> </properties> </persistence-unit> </persistence>
Building environment specific artifacts with classifier
TweetPosted on Friday Mar 20, 2015 at 03:09PM in Maven
Consider following multi-module Maven project:
-
classifier
: The parent & aggregator project -
persistence
: A jar project which holds JPA entities and a persistence descriptor (persistence.xml
) -
web
: A war project which depends onpersistence
project
persistence
project contains following persistence descriptor:
I need to set javax.persistence.schema-generation.database.action
property as drop-and-create
for development environment, but none
for production environment. in such case, using profiles and filtering might be a solution, but its shortcoming is that we can hold only one (among environment specific builds) artifact in the local repository because these builds has same coordinate. it may bring unexpected result such as deploying development build to the production environment by some accident. in such case, using classifier would be a better solution.
Preparation
First, let’s enable resource filtering in persistence
project.
<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <!-- Filter and copy resources under src/main/resources into target/classes (default location) --> <execution> <id>default-resources</id> <phase>process-resources</phase> <goals> <goal>resources</goal> </goals> <configuration> <filters> <filter>${basedir}/filters/dev.properties</filter> </filters> </configuration> </execution> </executions> </plugin> </plugins> </build>
Then put filters/dev.properties
:
javax.persistence.schema-generation.database.action=drop-and-create
Set a dependency in web
project:
<dependencies> <dependency> <groupId>org.nailedtothex.examples.classifier</groupId> <artifactId>persistence</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
Add configurations for producing artifacts for production
Now we can make artifacts that holds filtered persistence.xml
for development environment. next, let’s add configurations for producing artifacts for production environment with prod
classifier.
Put following profile definition into persistence
project to make the project to produce both of development and production (with prod
classifier) artifacts:
<profile> <id>prod</id> <properties> <filteredResources>target/filtered-classes</filteredResources> </properties> <build> <plugins> <plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <!-- Filter and copy resources under src/main/resources into target/filtered-classes/prod --> <execution> <id>prod-resources</id> <phase>process-resources</phase> <goals> <goal>resources</goal> </goals> <configuration> <outputDirectory>${filteredResources}/prod</outputDirectory> <filters> <filter>${basedir}/filters/prod.properties</filter> </filters> </configuration> </execution> <!-- Copy classes under target/classes into target/filtered-classes/prod --> <!-- Existing files will not be overwritten. --> <!-- see http://maven.apache.org/plugins/maven-resources-plugin/resources-mojo.html#overwrite --> <execution> <id>copy-classes-prod</id> <phase>process-classes</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>${filteredResources}/prod</outputDirectory> <resources> <resource> <directory>${project.build.outputDirectory}</directory> <filtering>false</filtering> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <executions> <!-- Create the production jar with files inside target/filtered-classes/prod --> <execution> <id>prod-jar</id> <phase>package</phase> <goals> <goal>jar</goal> </goals> <configuration> <classifier>prod</classifier> <classesDirectory>${filteredResources}/prod</classesDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile>
Also put filters/prod.properties
:
javax.persistence.schema-generation.database.action=none
Issue following command:
$ mvn clean install -P prod
Result:
... [INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ persistence --- [INFO] Building jar: /Users/kyle/src/classifier/persistence/target/persistence-1.0-SNAPSHOT.jar [INFO] [INFO] --- maven-jar-plugin:2.6:jar (prod-jar) @ persistence --- [INFO] Building jar: /Users/kyle/src/classifier/persistence/target/persistence-1.0-SNAPSHOT-prod.jar [INFO] [INFO] --- maven-install-plugin:2.4:install (default-install) @ persistence --- [INFO] Installing /Users/kyle/src/classifier/persistence/target/persistence-1.0-SNAPSHOT.jar to /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/persistence/1.0-SNAPSHOT/persistence-1.0-SNAPSHOT.jar [INFO] Installing /Users/kyle/src/classifier/persistence/pom.xml to /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/persistence/1.0-SNAPSHOT/persistence-1.0-SNAPSHOT.pom [INFO] Installing /Users/kyle/src/classifier/persistence/target/persistence-1.0-SNAPSHOT-prod.jar to /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/persistence/1.0-SNAPSHOT/persistence-1.0-SNAPSHOT-prod.jar ...
You can see both of artifacts are installed as expected:
$ unzip -p /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/persistence/1.0-SNAPSHOT/persistence-1.0-SNAPSHOT.jar META-INF/persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="myPU"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <properties> <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/> </properties> </persistence-unit> </persistence> $ unzip -p /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/persistence/1.0-SNAPSHOT/persistence-1.0-SNAPSHOT-prod.jar META-INF/persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="myPU"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <properties> <property name="javax.persistence.schema-generation.database.action" value="none"/> </properties> </persistence-unit> </persistence>
So what is needed for web
project? put following profile as well:
<profile> <id>prod</id> <dependencies> <dependency> <groupId>org.nailedtothex.examples.classifier</groupId> <artifactId>persistence</artifactId> <version>1.0-SNAPSHOT</version> <classifier>prod</classifier> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <executions> <execution> <id>default-war</id> <phase>package</phase> <goals> <goal>war</goal> </goals> <configuration> <packagingExcludes>WEB-INF/lib/persistence-1.0-SNAPSHOT-prod.jar</packagingExcludes> </configuration> </execution> <execution> <id>prod-war</id> <phase>package</phase> <goals> <goal>war</goal> </goals> <configuration> <classifier>prod</classifier> <packagingExcludes>WEB-INF/lib/persistence-1.0-SNAPSHOT.jar</packagingExcludes> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile>
Then you will get both of artifacts installed after issuing mvn clean install -P prod
as follows:
$ unzip -l /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/web/1.0-SNAPSHOT/web-1.0-SNAPSHOT.war | grep persistence 3521 03-20-15 14:25 WEB-INF/lib/persistence-1.0-SNAPSHOT.jar $ unzip -l /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/web/1.0-SNAPSHOT/web-1.0-SNAPSHOT-prod.war | grep persistence 3513 03-20-15 14:25 WEB-INF/lib/persistence-1.0-SNAPSHOT-prod.jar
An example of Maven EAR project consists of an EJB interface, an EJB implementation and a WAR
TweetPosted on Friday Mar 06, 2015 at 10:43PM in Maven
The project consists of following principal modules:
-
eartest-ejb-api: holds an EJB local interface named
Hello
. packaging=jar. no dependency. -
eartest-ejb-impl: holds an EJB implementation named
HelloImpl
which implementsHello
. packaging=ejb. depends on eartest-ejb-api. -
eartest-war: holds an Servlet which has an injection point of
Hello
interface. depends on eartest-ejb-api. -
eartest-ear: holds above 3 modules in the EAR.
Whole project is can be obtained from https://github.com/lbtc-xxx/eartest .
Structure of eartest-ear module
$ tree eartest-ear/target/eartest-ear eartest-ear/target/eartest-ear |-- META-INF | `-- application.xml |-- eartest-ejb-impl-1.0-SNAPSHOT.jar |-- eartest-war-1.0-SNAPSHOT.war `-- lib `-- eartest-ejb-api-1.0-SNAPSHOT.jar 2 directories, 4 files
Structure of eartest-war module
$ tree eartest-war/target/eartest-war eartest-war/target/eartest-war |-- META-INF `-- WEB-INF `-- classes `-- eartest `-- war `-- MyServlet.class 5 directories, 1 file
MyServlet can reference eartest-ejb-api-1.0-SNAPSHOT.jar
because it’s placed under lib
directory in the parent EAR. this packaging style is called as Skinny WAR.
Structure of eartest-ejb-api
$ tree eartest-ejb-api/target/classes eartest-ejb-api/target/classes `-- eartest `-- ejb `-- api `-- Hello.class 3 directories, 1 file
Structure of eartest-ejb-impl
$ tree eartest-ejb-impl/target/classes eartest-ejb-impl/target/classes |-- META-INF | `-- ejb-jar.xml `-- eartest `-- ejb `-- impl `-- HelloImpl.class 4 directories, 2 files
A problem with IntelliJ IDEA
IntelliJ has an annoying issue: Maven support cannot handle skinny wars for EAR deployments : IDEA-97324. this brings unnecessary eartest-ejb-api into WEB-INF/lib
inside the WAR and brings following exception. to avoid this, I need to put <scope>provided</scope>
in dependency declaration for eartest-ejb-api
in pom.xml
of eartest-war
.
Caused by: java.lang.IllegalStateException: JBAS011048: Failed to construct component instance at org.jboss.as.ee.component.BasicComponent.constructComponentInstance(BasicComponent.java:162) at org.jboss.as.ee.component.BasicComponent.constructComponentInstance(BasicComponent.java:133) at org.jboss.as.ee.component.BasicComponent.createInstance(BasicComponent.java:89) at org.jboss.as.ee.component.ComponentRegistry$ComponentManagedReferenceFactory.getReference(ComponentRegistry.java:149) at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$5.createInstance(UndertowDeploymentInfoService.java:1233) at io.undertow.servlet.core.ManagedServlet$DefaultInstanceStrategy.start(ManagedServlet.java:215) [undertow-servlet-1.1.0.Final.jar:1.1.0.Final] ... 27 more Caused by: java.lang.IllegalArgumentException: Can not set eartest.ejb.api.Hello field eartest.war.MyServlet.hello to eartest.ejb.api.Hello$$$view17 at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167) [rt.jar:1.8.0_20] at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171) [rt.jar:1.8.0_20] at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81) [rt.jar:1.8.0_20] at java.lang.reflect.Field.set(Field.java:758) [rt.jar:1.8.0_20] at org.jboss.as.ee.component.ManagedReferenceFieldInjectionInterceptorFactory$ManagedReferenceFieldInjectionInterceptor.processInvocation(ManagedReferenceFieldInjectionInterceptorFactory.java:108) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309) at org.jboss.invocation.WeavedInterceptor.processInvocation(WeavedInterceptor.java:53) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309) at org.jboss.as.ee.component.AroundConstructInterceptorFactory$1.processInvocation(AroundConstructInterceptorFactory.java:28) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309) at org.jboss.as.ee.concurrent.ConcurrentContextInterceptor.processInvocation(ConcurrentContextInterceptor.java:45) [wildfly-ee-8.2.0.Final.jar:8.2.0.Final] at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309) at org.jboss.invocation.ContextClassLoaderInterceptor.processInvocation(ContextClassLoaderInterceptor.java:64) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309) at org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:326) at org.jboss.invocation.PrivilegedWithCombinerInterceptor.processInvocation(PrivilegedWithCombinerInterceptor.java:80) at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309) at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61) at org.jboss.as.ee.component.BasicComponent.constructComponentInstance(BasicComponent.java:160) ... 32 more