We’ve discussed the Deployment to Glassfish 3.x in Part 3 of the series. In this article, we’ll do Maven deployment to JBoss AS 5.x and JBoss AS 6.x. For readers regardless if you are new to the series or had been following it, you don’t have to finish reading all the article parts before coming to this, all you have to do is to first complete or understand Part 1 and Part 2 of the series (you can skip Part 3) before carrying out the deployment steps for JBoss Application Server in this article.
This article deals with the deployment to both JBoss AS 5.x and JBoss AS 6.x in the same approach in the perspective of Maven, as we will be using the same Maven plugin in our EAR module for both JBoss AS 5.x and JBoss AS 6.x. I’ll outline to you the necessary preparation and little changes on the codes (only for the integration-test module) in what you have carried over from Part 2 to accommodate the deployment requirements.
Warning On Compatibility:
The contents outlined in the article are NOT compatible with JBoss AS 7.x.
Preparation
JBoss Application Server 5.x & 6.x Setup Brief
The setup is relatively straight forward. This is what we need to do:
- In this setup, we’ll create a separate server instance/domain name call dummyserver-standard.
- We’ll create a mysql-ds.xml file to describe the data source properties for the connection to the MySQL database, the DB which is used through out this demo. If you are using other DB, please create a different *-ds.xml file by referring to JBoss’s documentation on data source setup.
- As for setting up the respective JMS connection factories and queue destination, we’ll deal with this setup separately for both JBoss AS 5.x and JBoss AS 6.x because JBoss AS 6.x uses HornetQ as their messaging module, which is different from JBoss AS 5.x.
Maven Repositories & Dependencies
Before touching the application servers, let’s get the initial things right, this includes the maven repository definition and the correct dependencies on the pom.xml files. Please edit the root pom.xml file, located at <path<DummyDemoJavaEE5/pom.xml and change it to the below contents:
| XML | | copy code | | ? |
| 01 | <?xml version="1.0" encoding="UTF-8"?> |
| 02 | <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"> |
| 03 | <!-- Basic properties --> |
| 04 | <modelVersion>4.0.0</modelVersion> |
| 05 | <groupId>com.developerscrappad</groupId> |
| 06 | <artifactId>DummyDemoJavaEE5</artifactId> |
| 07 | <version>1.0-SNAPSHOT</version> |
| 08 | <name>DummyDemoJavaEE5</name> |
| 09 | |
| 10 | <!-- Please take note of the packaging type --> |
| 11 | <packaging>pom</packaging> |
| 12 | |
| 13 | <!-- For any sub-modules (one level below), just include it here --> |
| 14 | <modules> |
| 15 | <module>project</module> |
| 16 | <module>integration-test</module> |
| 17 | </modules> |
| 18 | |
| 19 | <repositories> |
| 20 | <repository> |
| 21 | <id>jboss</id> |
| 22 | <url>https://repository.jboss.org/nexus/</url> |
| 23 | </repository> |
| 24 | <repository> |
| 25 | <id>eclipse</id> |
| 26 | <url>http://download.eclipse.org/rt/eclipselink/maven.repo</url> |
| 27 | </repository> |
| 28 | <repository> |
| 29 | <id>jboss-3p-release</id> |
| 30 | <url>https://repository.jboss.org/nexus/content/repositories/thirdparty-releases</url> |
| 31 | </repository> |
| 32 | <repository> |
| 33 | <id>jboss-3p-uploads</id> |
| 34 | <url>https://repository.jboss.org/nexus/content/repositories/thirdparty-uploads</url> |
| 35 | </repository> |
| 36 | <repository> |
| 37 | <id>jboss-deprecated</id> |
| 38 | <url>https://repository.jboss.org/nexus/content/repositories/deprecated</url> |
| 39 | </repository> |
| 40 | <repository> |
| 41 | <id>Atlassian</id> |
| 42 | <url>https://repository.atlassian.com/maven2</url> |
| 43 | </repository> |
| 44 | </repositories> |
| 45 | |
| 46 | <dependencies> |
| 47 | <!-- For testing purposes --> |
| 48 | <dependency> |
| 49 | <groupId>junit</groupId> |
| 50 | <artifactId>junit</artifactId> |
| 51 | <version>4.10</version> |
| 52 | <scope>test</scope> |
| 53 | </dependency> |
| 54 | </dependencies> |
| 55 | </project> |
Have you notice the increase of repository URLs? In fact, for plain vanilla deployment, we don’t need that much of external repositories to be included, but because we are running the jboss-as-client within the integration-test module, it notoriously relies on many other dependencies and that is why we have to put these repository URLs as part of the reliance for integration test. We’ll look into the integration test later.
Using & Configuring The JBoss-Maven-Plugin
In order to deploy the EAR to the either the JBoss 5.x or 6.x App Server, we need the JBoss-Maven-Plugin. You may go to the plugin’s website and make a detail study of it. Anyway, here’s how to have the plugin configured with a very comprehensive config options which could be used in most deployment scenario. We’ll define the plugin in the DummyDemo-ear’s pom.xml file.
Please edit/replace the <path>/DummyDemoJavaEE5/project/DummyDemo-ear/pom.xml to the below:
| XML | | copy code | | ? |
| 001 | <?xml version="1.0" encoding="UTF-8"?> |
| 002 | <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"> |
| 003 | |
| 004 | <!-- Basic Properties --> |
| 005 | <modelVersion>4.0.0</modelVersion> |
| 006 | <groupId>com.developerscrappad</groupId> |
| 007 | <artifactId>DummyDemo-ear</artifactId> |
| 008 | <version>1.0-SNAPSHOT</version> |
| 009 | <name>DummyDemo-ear</name> |
| 010 | |
| 011 | <!-- The absolute path to the JBoss Server, please change this... --> |
| 012 | <properties> |
| 013 | <jboss.directory>C:/Java/jboss-5.1.0</jboss.directory> |
| 014 | </properties> |
| 015 | |
| 016 | <!-- Reference the parent --> |
| 017 | <parent> |
| 018 | <artifactId>project</artifactId> |
| 019 | <groupId>com.developerscrappad</groupId> |
| 020 | <version>1.0-SNAPSHOT</version> |
| 021 | </parent> |
| 022 | |
| 023 | <!-- The packaging value of any EAR module should be "ear" --> |
| 024 | <packaging>ear</packaging> |
| 025 | |
| 026 | <!-- |
| 027 | The dependencies for EAR module should include all thoese sub-modules |
| 028 | that are needed to be packaged into the ear file like the below... |
| 029 | --> |
| 030 | <dependencies> |
| 031 | <dependency> |
| 032 | <groupId>com.developerscrappad</groupId> |
| 033 | <artifactId>DummyDemo-ejb</artifactId> |
| 034 | <version>1.0-SNAPSHOT</version> |
| 035 | <type>ejb</type> |
| 036 | </dependency> |
| 037 | <dependency> |
| 038 | <groupId>com.developerscrappad</groupId> |
| 039 | <artifactId>DummyDemo-web</artifactId> |
| 040 | <version>1.0-SNAPSHOT</version> |
| 041 | <type>war</type> |
| 042 | </dependency> |
| 043 | <dependency> |
| 044 | <groupId>com.developerscrappad</groupId> |
| 045 | <artifactId>DummyDemo-appclient</artifactId> |
| 046 | <version>1.0-SNAPSHOT</version> |
| 047 | <type>app-client</type> |
| 048 | </dependency> |
| 049 | <dependency> |
| 050 | <groupId>com.developerscrappad</groupId> |
| 051 | <artifactId>DummyDemo-api</artifactId> |
| 052 | <version>1.0-SNAPSHOT</version> |
| 053 | <type>jar</type> |
| 054 | </dependency> |
| 055 | </dependencies> |
| 056 | |
| 057 | <build> |
| 058 | <plugins> |
| 059 | <plugin> |
| 060 | <groupId>org.apache.maven.plugins</groupId> |
| 061 | <artifactId>maven-compiler-plugin</artifactId> |
| 062 | <version>2.0.2</version> |
| 063 | <configuration> |
| 064 | <source>1.5</source> |
| 065 | <target>1.5</target> |
| 066 | </configuration> |
| 067 | </plugin> |
| 068 | <!-- |
| 069 | The ear plugin must include the definition for "modules", |
| 070 | for any modules that are needed to be included in the EAR file, |
| 071 | you need to specify them one by one, e.g. "ejbModule", "webModule", |
| 072 | "jarModule" and "appClientModule". |
| 073 | --> |
| 074 | <plugin> |
| 075 | <groupId>org.apache.maven.plugins</groupId> |
| 076 | <artifactId>maven-ear-plugin</artifactId> |
| 077 | <version>2.6</version> |
| 078 | <configuration> |
| 079 | <version>5</version> |
| 080 | <defaultLibBundleDir>lib</defaultLibBundleDir> |
| 081 | <modules> |
| 082 | <ejbModule> |
| 083 | <groupId>com.developerscrappad</groupId> |
| 084 | <artifactId>DummyDemo-ejb</artifactId> |
| 085 | </ejbModule> |
| 086 | <webModule> |
| 087 | <groupId>com.developerscrappad</groupId> |
| 088 | <artifactId>DummyDemo-web</artifactId> |
| 089 | <context-root>/DummyDemo-web</context-root> |
| 090 | </webModule> |
| 091 | <jarModule> |
| 092 | <groupId>com.developerscrappad</groupId> |
| 093 | <artifactId>DummyDemo-api</artifactId> |
| 094 | </jarModule> |
| 095 | <appClientModule> |
| 096 | <groupId>com.developerscrappad</groupId> |
| 097 | <artifactId>DummyDemo-appclient</artifactId> |
| 098 | </appClientModule> |
| 099 | </modules> |
| 100 | </configuration> |
| 101 | </plugin> |
| 102 | |
| 103 | <!-- JBoss App Server --> |
| 104 | <plugin> |
| 105 | <groupId>org.codehaus.mojo</groupId> |
| 106 | <artifactId>jboss-maven-plugin</artifactId> |
| 107 | <version>1.5.0</version> |
| 108 | <executions> |
| 109 | <execution> |
| 110 | <id>jboss-undeploy</id> |
| 111 | <goals> |
| 112 | <goal>undeploy</goal> |
| 113 | </goals> |
| 114 | <phase>clean</phase> |
| 115 | </execution> |
| 116 | <execution> |
| 117 | <id>jboss-deploy</id> |
| 118 | <goals> |
| 119 | <goal>deploy</goal> |
| 120 | </goals> |
| 121 | <phase>package</phase> |
| 122 | </execution> |
| 123 | </executions> |
| 124 | <configuration> |
| 125 | <jbossHome>${jboss.directory}</jbossHome> |
| 126 | <serverName>dummyserver-standard</serverName> |
| 127 | <hostName>localhost</hostName> |
| 128 | <port>8080</port> |
| 129 | <fileNames> |
| 130 | <fileName>${basedir}/target/DummyDemo-ear-1.0-SNAPSHOT.ear</fileName> |
| 131 | </fileNames> |
| 132 | </configuration> |
| 133 | </plugin> |
| 134 | </plugins> |
| 135 | </build> |
| 136 | |
| 137 | </project> |
pom.xml Explained:
Looking at the edited pom.xml, I have defined a property at the top called jboss.directory, with the value that points to the absolute path of the JBoss App Server directory. Please change this value accordingly. Moving down to the plugin section, both the “deploy” and “undeploy” plugin goals will only be executed during the package phase (I want to make the EAR deployed to the application server just after packing everything to the EAR file) and the clean build cycle respectively.
As for the plugin configurations, there are many options to be configured, which you can find them at the plugin page. But as for now, the above is sufficient. However, do take note on a few options listed below:
| Option | Description |
|---|---|
| jbossHome | The value of <jbossHome> has to be of the absolute path of the directory that the JBoss Application Server resides. Here, we can directly assign the value from ${jboss.directory}. |
| serverName | The name of the server instance/domain that this plugin should be referring. For this demo project, the value should be dummyserver-standard. |
| hostName | The IP address of the hostname of the target application server. |
| port | The port number for the application server’s admin-console or jmx-console, which usually is defaulted to 8080. |
| fileNames -> fileName | The absolute path of the EAR to be pickup for deployment. |
JBoss: Creating The Server Domain/Instance
I prefer to do this manually without relying on automation from the JBoss-Maven-Plugin. We will use the “standard” server instance configuration from either JBoss AS 5.x or JBoss AS 6.x in this demo. So, just perform the following:
- Please create a new directory by the name of “dummyserver-standard” in the location
<JBOSS_HOME>/server/
- Copy all the contents of <JBOSS_HOME>/server/standard and place it in the new “dummyserver-standard” directory that you have just created.
JBoss: Configuring the DB Data Source
Since we are using the MySQL DB for this demo, just create the mysql-ds.xml file with the below contents and put it into the <JBOSS_HOME>/server/dummyserver-standard/deploy/ directory.
| XML | | copy code | | ? |
| 01 | <?xml version="1.0" encoding="UTF-8"?> |
| 02 | <datasources> |
| 03 | <local-tx-datasource> |
| 04 | <jndi-name>jndi/DummyDemoMySQLDB</jndi-name> |
| 05 | |
| 06 | <!-- Please change the url accordingly --> |
| 07 | <connection-url>jdbc:mysql://localhost:3306/DUMMY_DEMO_DB</connection-url> |
| 08 | <driver-class>com.mysql.jdbc.Driver</driver-class> |
| 09 | |
| 10 | <user-name>root</user-name> <!-- Please change this accordingly --> |
| 11 | <password>your_password</password> <!-- Please change this accordingly --> |
| 12 | |
| 13 | <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name> |
| 14 | <metadata> |
| 15 | <type-mapping>mySQL</type-mapping> |
| 16 | </metadata> |
| 17 | </local-tx-datasource> |
| 18 | </datasources> |
Don’t forget the place the JDBC driver in the <JBOSS_HOME>/server/dummyserver-standard/lib/ directory.
JBoss: Configuring the JMS Queue and Connection Factory
We need to create both Queue Connection Factory and the Queue destination for the DummyDemoJavaEE5 application. To do this, just following the instructions below:
JBoss AS 5.x Specific
- Go to the directory <JBOSS_HOME>/server/dummyserver-standard/deploy/messaging/ and edit the file connection-factories.xml, keep the current default contents as it is, but insert the below within the <server> tag:
XML | copy code | ? 01 <!-- Queue Connection Factory -->02 <mbean code="org.jboss.jms.server.connectionfactory.ConnectionFactory"03 name="jboss.messaging.connectionfactory:service=TestQueueConnectionFactory"04 xmbean-dd="xmdesc/ConnectionFactory-xmbean.xml">05 <constructor>06 <arg type="java.lang.String" value="TestQueueConnectionFactory"/>07 </constructor>08 <depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer</depends>
09 <depends optional-attribute-name="Connector">jboss.messaging:service=Connector,transport=bisocket</depends>
10 <depends>jboss.messaging:service=PostOffice</depends>
11 <attribute name="PrefetchSize">150</attribute>
12 <attribute name="DupsOKBatchSize">5000</attribute>
13 <attribute name="SupportsFailover">false</attribute>
14 <attribute name="SupportsLoadBalancing">false</attribute>
15 <attribute name="SlowConsumers">false</attribute>
16 <attribute name="JNDIBindings">17 <bindings>18 <binding>jms/TestQueueConnectionFactory</binding>
19 </bindings>20 </attribute>21 </mbean>
Safe the changes. - Now, edit the destionations-service.xml file in the <JBOSS_HOME>/server/dummyserver-standard/deploy/messaging/ directory. Keep the default contents as it is, but insert the below portion within the <server> tag:
XML | copy code | ? 01 <!-- Queue -->02 <mbean code="org.jboss.jms.server.destination.QueueService"03 name="jboss.messaging.destination:service=Queue,name=TestQueue"04 xmbean-dd="xmdesc/Queue-xmbean.xml">05 <depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer</depends>
06 <attribute name="DLQ">07 jboss.messaging.destination:service=Queue,name=PrivateDLQ
08 </attribute>09 <attribute name="ExpiryQueue">10 jboss.messaging.destination:service=Queue,name=PrivateExpiryQueue
11 </attribute>12 <attribute name="JNDIName">jms/TestQueue</attribute>
13 <attribute name="RedeliveryDelay">1500</attribute>
14 <attribute name="MaxDeliveryAttempts">5</attribute>
15 <attribute name="FullSize">50000</attribute>
16 <attribute name="PageSize">5000</attribute>
17 <attribute name="DownCacheSize">2500</attribute>
18 <attribute name="MaxSize">75000</attribute>
19 <attribute name="SecurityConfig">20 <security>21 <role name="guest" read="true" write="true"/>22 <role name="publisher" read="true" write="true" create="false"/>23 </security>24 </attribute>25 </mbean>
This will do for now on JBoss 5.x.
** For more information, please visit: Quick Note: How To Setup JMS Queue/Topic & Connection Factory On JBoss 5.x
JBoss AS 6.x Specific
Beginning from JBoss AS 6.x, the JBoss team had adopted the HornetQ as the messaging engine for JBoss AS. Just follow the below to get the queue and connection factory up and running:
- Change the working directory to <JBOSS_HOME>/server/dummyserver-standard/deploy/
- Create a file call dummy-hornetq-jms.xml (any jms related configuration file must have the filename suffix of -hornetq-jms.xml) and have the xml contents as the below:
XML | copy code | ? 01 <configuration xmlns="urn:hornetq"02 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"03 xsi:schemaLocation="urn:hornetq /schema/hornetq-jms.xsd">04 05 <!-- Queue Connection Factory -->06 <connection-factory name="TestQueueConnectionFactory">07 <xa>false</xa>
08 <connectors>09 <connector-ref connector-name="netty"/>10 </connectors>11 <entries>12 <entry name="jms/TestQueueConnectionFactory"/>13 </entries>14 </connection-factory>15 16 <!-- Queue Destination -->17 <queue name="TestQueue">18 <entry name="jms/TestQueue"/>19 </queue>20 21 </configuration>
This is for the JBoss AS 6.x.
** For more information on setting up Queues, Topic and Connection Factories on JBoss AS 6.x, please visit: Quick Note: How To Setup JMS Queue/Topic & Connection Factory On JBoss 6.x
The Right Dependency For Integration Test
Before deployment and performing the integration test, I would just want to highlight to you that we will be using the jboss-as-client as the depending library for running the integration test. If you check on mvnrepository.com, the jboss-as-client as several versions. For JBoss AS 5.1.0, we’ll use jboss-as-client version 5.1.0.GA; for JBoss AS 6.1.0, we’ll use the version 6.1.0.Final for the jboss-as-client.
Please edit the pom.xml in <path>/DummyDemoJavaEE5/integration-test/ with the below contents:
JBoss AS 5.1.0 Specific:
| XML | | copy code | | ? |
| 01 | <?xml version="1.0"?> |
| 02 | <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" |
| 03 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
| 04 | |
| 05 | <!-- Basic Properties --> |
| 06 | <modelVersion>4.0.0</modelVersion> |
| 07 | <groupId>com.developerscrappad</groupId> |
| 08 | <artifactId>integration-test</artifactId> |
| 09 | <version>1.0-SNAPSHOT</version> |
| 10 | <name>integration-test</name> |
| 11 | |
| 12 | <!-- Reference the parent --> |
| 13 | <parent> |
| 14 | <groupId>com.developerscrappad</groupId> |
| 15 | <artifactId>DummyDemoJavaEE5</artifactId> |
| 16 | <version>1.0-SNAPSHOT</version> |
| 17 | </parent> |
| 18 | |
| 19 | <!-- Define the necessary dependencies for running the integration test --> |
| 20 | <dependencies> |
| 21 | <dependency> |
| 22 | <groupId>org.jboss.jbossas</groupId> |
| 23 | <artifactId>jboss-as-client</artifactId> |
| 24 | <version>5.1.0.GA</version> |
| 25 | <type>pom</type> |
| 26 | <scope>test</scope> |
| 27 | </dependency> |
| 28 | <dependency> |
| 29 | <groupId>com.developerscrappad</groupId> |
| 30 | <artifactId>DummyDemo-ejb</artifactId> |
| 31 | <version>1.0-SNAPSHOT</version> |
| 32 | <type>ejb</type> |
| 33 | <scope>test</scope> |
| 34 | </dependency> |
| 35 | </dependencies> |
| 36 | |
| 37 | <build> |
| 38 | <plugins> |
| 39 | <!-- |
| 40 | Sine this is an integration test module, the Failsafe plugin is now use. |
| 41 | It will only pickup the integration files from the **/itest/ directory |
| 42 | when running the integration-test. Please do not confuse this with unit test. |
| 43 | --> |
| 44 | <plugin> |
| 45 | <groupId>org.apache.maven.plugins</groupId> |
| 46 | <artifactId>maven-failsafe-plugin</artifactId> |
| 47 | <version>2.12.4</version> |
| 48 | <executions> |
| 49 | <execution> |
| 50 | <id>failsafe1</id> |
| 51 | <goals> |
| 52 | <goal>integration-test</goal> |
| 53 | </goals> |
| 54 | <phase>integration-test</phase> |
| 55 | </execution> |
| 56 | <execution> |
| 57 | <id>failsafe2</id> |
| 58 | <goals> |
| 59 | <goal>verify</goal> |
| 60 | </goals> |
| 61 | <phase>verify</phase> |
| 62 | </execution> |
| 63 | </executions> |
| 64 | <configuration> |
| 65 | <includes> |
| 66 | <include>**/itest/*.java</include> |
| 67 | </includes> |
| 68 | </configuration> |
| 69 | </plugin> |
| 70 | |
| 71 | <!-- |
| 72 | The surefire plugin is for unit testing purposes. |
| 73 | For unit test, it will only pickup files within the **/utest/ directory. |
| 74 | So, make sure you know where to place your unit test java files. |
| 75 | --> |
| 76 | <plugin> |
| 77 | <groupId>org.apache.maven.plugins</groupId> |
| 78 | <artifactId>maven-surefire-plugin</artifactId> |
| 79 | <version>2.12.4</version> |
| 80 | <executions> |
| 81 | <execution> |
| 82 | <id>sf1</id> |
| 83 | <goals> |
| 84 | <goal>test</goal> |
| 85 | </goals> |
| 86 | <phase>test</phase> |
| 87 | </execution> |
| 88 | </executions> |
| 89 | <configuration> |
| 90 | <includes> |
| 91 | <include>**/utest/*.java</include> |
| 92 | </includes> |
| 93 | </configuration> |
| 94 | </plugin> |
| 95 | </plugins> |
| 96 | </build> |
| 97 | </project> |
JBoss AS 6.1.0 Specific:
| XML | | copy code | | ? |
| 001 | <?xml version="1.0"?> |
| 002 | <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" |
| 003 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
| 004 | |
| 005 | <!-- Basic Properties --> |
| 006 | <modelVersion>4.0.0</modelVersion> |
| 007 | <groupId>com.developerscrappad</groupId> |
| 008 | <artifactId>integration-test</artifactId> |
| 009 | <version>1.0-SNAPSHOT</version> |
| 010 | <name>integration-test</name> |
| 011 | |
| 012 | <!-- Reference the parent --> |
| 013 | <parent> |
| 014 | <groupId>com.developerscrappad</groupId> |
| 015 | <artifactId>DummyDemoJavaEE5</artifactId> |
| 016 | <version>1.0-SNAPSHOT</version> |
| 017 | </parent> |
| 018 | |
| 019 | <!-- Define the necessary dependencies for running the integration test --> |
| 020 | <dependencies> |
| 021 | <dependency> |
| 022 | <groupId>org.jboss.jbossas</groupId> |
| 023 | <artifactId>jboss-as-client</artifactId> |
| 024 | <version>6.1.0.Final</version> |
| 025 | <type>pom</type> |
| 026 | <scope>test</scope> |
| 027 | </dependency> |
| 028 | <dependency> |
| 029 | <groupId>org.jboss.jbossas</groupId> |
| 030 | <artifactId>jboss-as-hornetq-int</artifactId> |
| 031 | <version>6.1.0.Final</version> |
| 032 | <scope>test</scope> |
| 033 | </dependency> |
| 034 | <dependency> |
| 035 | <groupId>com.developerscrappad</groupId> |
| 036 | <artifactId>DummyDemo-ejb</artifactId> |
| 037 | <version>1.0-SNAPSHOT</version> |
| 038 | <type>ejb</type> |
| 039 | <scope>test</scope> |
| 040 | </dependency> |
| 041 | </dependencies> |
| 042 | |
| 043 | <build> |
| 044 | <plugins> |
| 045 | <!-- |
| 046 | Sine this is an integration test module, the Failsafe plugin is now use. |
| 047 | It will only pickup the integration files from the **/itest/ directory |
| 048 | when running the integration-test. Please do not confuse this with unit test. |
| 049 | --> |
| 050 | <plugin> |
| 051 | <groupId>org.apache.maven.plugins</groupId> |
| 052 | <artifactId>maven-failsafe-plugin</artifactId> |
| 053 | <version>2.12.4</version> |
| 054 | <executions> |
| 055 | <execution> |
| 056 | <id>failsafe1</id> |
| 057 | <goals> |
| 058 | <goal>integration-test</goal> |
| 059 | </goals> |
| 060 | <phase>integration-test</phase> |
| 061 | </execution> |
| 062 | <execution> |
| 063 | <id>failsafe2</id> |
| 064 | <goals> |
| 065 | <goal>verify</goal> |
| 066 | </goals> |
| 067 | <phase>verify</phase> |
| 068 | </execution> |
| 069 | </executions> |
| 070 | <configuration> |
| 071 | <includes> |
| 072 | <include>**/itest/*.java</include> |
| 073 | </includes> |
| 074 | </configuration> |
| 075 | </plugin> |
| 076 | |
| 077 | <!-- |
| 078 | The surefire plugin is for unit testing purposes. |
| 079 | For unit test, it will only pickup files within the **/utest/ directory. |
| 080 | So, make sure you know where to place your unit test java files. |
| 081 | --> |
| 082 | <plugin> |
| 083 | <groupId>org.apache.maven.plugins</groupId> |
| 084 | <artifactId>maven-surefire-plugin</artifactId> |
| 085 | <version>2.12.4</version> |
| 086 | <executions> |
| 087 | <execution> |
| 088 | <id>sf1</id> |
| 089 | <goals> |
| 090 | <goal>test</goal> |
| 091 | </goals> |
| 092 | <phase>test</phase> |
| 093 | </execution> |
| 094 | </executions> |
| 095 | <configuration> |
| 096 | <includes> |
| 097 | <include>**/utest/*.java</include> |
| 098 | </includes> |
| 099 | </configuration> |
| 100 | </plugin> |
| 101 | </plugins> |
| 102 | </build> |
| 103 | </project> |
A little changes in integration test codes:
Not forgetting the context environment properties in the integration test classes, we have to change the environment properties of the below files at the constructor. I have place the modified unit test files found in <path>/DummyDemoJavaEE/integration-test/src/test/com/developerscrappad/itest/ at the below:
Codes for com.developerscrappad.itest.BMPIntegrationTest.java
| Java(TM) 2 Platform Standard Edition 5.0 | | copy code | | ? |
| 001 | package com.developerscrappad.itest; |
| 002 | |
| 003 | import com.developerscrappad.entity.AppTable1Model; |
| 004 | import com.developerscrappad.entity.AppTable2Model; |
| 005 | import com.developerscrappad.entity.AppTable3Model; |
| 006 | import com.developerscrappad.intf.IBMPFacadeRemote; |
| 007 | import com.developerscrappad.intf.ICMPFacadeRemote; |
| 008 | import java.util.Properties; |
| 009 | import javax.naming.Context; |
| 010 | import javax.naming.InitialContext; |
| 011 | import org.junit.After; |
| 012 | import org.junit.Before; |
| 013 | import org.junit.Test; |
| 014 | import static org.junit.Assert.*; |
| 015 | |
| 016 | public class BMPIntegrationTest { |
| 017 | |
| 018 | private Properties env; |
| 019 | private Context ctx; |
| 020 | |
| 021 | public BMPIntegrationTest() { |
| 022 | env = new Properties(); |
| 023 | env.setProperty( "java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory" ); |
| 024 | env.setProperty( "java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces" ); |
| 025 | env.setProperty( Context.PROVIDER_URL, "jnp://localhost:1099" ); |
| 026 | } |
| 027 | |
| 028 | @Before |
| 029 | public void setUp() throws Exception { |
| 030 | ctx = new InitialContext( env ); |
| 031 | } |
| 032 | |
| 033 | @After |
| 034 | public void tearDown() throws Exception { |
| 035 | IBMPFacadeRemote bmpFacade = ( IBMPFacadeRemote ) ctx.lookup( IBMPFacadeRemote.JNDI_NAME ); |
| 036 | bmpFacade.cleanupTables(); |
| 037 | } |
| 038 | |
| 039 | @Test |
| 040 | public void testBMPProcessCommit() throws Exception { |
| 041 | AppTable1Model model1 = new AppTable1Model(); |
| 042 | model1.setContents( "contents1" ); |
| 043 | |
| 044 | AppTable2Model model2 = new AppTable2Model(); |
| 045 | model2.setContents( "contents2" ); |
| 046 | |
| 047 | AppTable3Model model3 = new AppTable3Model(); |
| 048 | model3.setContents( "contents3" ); |
| 049 | |
| 050 | IBMPFacadeRemote bmpFacade = ( IBMPFacadeRemote ) ctx.lookup( IBMPFacadeRemote.JNDI_NAME ); |
| 051 | bmpFacade.executeBMPProcess( model1, model2, model3 ); |
| 052 | |
| 053 | ICMPFacadeRemote cmpFacade = ( ICMPFacadeRemote ) ctx.lookup( ICMPFacadeRemote.JNDI_NAME ); |
| 054 | model1 = cmpFacade.findAppTable1ByContents( "contents1" ); |
| 055 | assertEquals( "contents1", model1.getContents() ); |
| 056 | |
| 057 | model2 = cmpFacade.findAppTable2ByContents( "contents2" ); |
| 058 | assertEquals( "contents2", model2.getContents() ); |
| 059 | |
| 060 | model3 = cmpFacade.findAppTable3ByContents( "contents3" ); |
| 061 | assertEquals( "contents3", model3.getContents() ); |
| 062 | } |
| 063 | |
| 064 | @Test |
| 065 | public void testBMPProcessRollback() throws Exception { |
| 066 | AppTable1Model model1 = new AppTable1Model(); |
| 067 | model1.setContents( "contents1" ); |
| 068 | |
| 069 | AppTable2Model model2 = new AppTable2Model(); |
| 070 | model2.setContents( "contents2" ); |
| 071 | |
| 072 | AppTable3Model model3 = new AppTable3Model(); |
| 073 | model3.setContents( "contents3" ); |
| 074 | |
| 075 | IBMPFacadeRemote bmpFacade = ( IBMPFacadeRemote ) ctx.lookup( IBMPFacadeRemote.JNDI_NAME ); |
| 076 | bmpFacade.executeBMPProcess( model1, model2, model3 ); |
| 077 | |
| 078 | model1 = new AppTable1Model(); |
| 079 | model1.setContents( "contents1.1" ); |
| 080 | |
| 081 | model2 = new AppTable2Model(); |
| 082 | model2.setContents( "contents2.2" ); |
| 083 | |
| 084 | model3 = new AppTable3Model(); |
| 085 | model3.setContents( "contents3" ); |
| 086 | |
| 087 | try { |
| 088 | bmpFacade.executeBMPProcess( model1, model2, model3 ); |
| 089 | fail(); |
| 090 | } catch ( Exception ex ) { |
| 091 | //absorb |
| 092 | } |
| 093 | |
| 094 | ICMPFacadeRemote cmpFacade = ( ICMPFacadeRemote ) ctx.lookup( ICMPFacadeRemote.JNDI_NAME ); |
| 095 | |
| 096 | try { |
| 097 | model1 = cmpFacade.findAppTable1ByContents( "contents1.1" ); |
| 098 | fail(); |
| 099 | } catch ( Exception ex ) { |
| 100 | //absorb |
| 101 | } |
| 102 | |
| 103 | model1 = cmpFacade.findAppTable1ByContents( "contents1" ); |
| 104 | assertEquals( "contents1", model1.getContents() ); |
| 105 | |
| 106 | try { |
| 107 | model2 = cmpFacade.findAppTable2ByContents( "contents2.2" ); |
| 108 | fail(); |
| 109 | } catch ( Exception ex ) { |
| 110 | //absorb |
| 111 | } |
| 112 | |
| 113 | model2 = cmpFacade.findAppTable2ByContents( "contents2" ); |
| 114 | assertEquals( "contents2", model2.getContents() ); |
| 115 | |
| 116 | model3 = cmpFacade.findAppTable3ByContents( "contents3" ); |
| 117 | assertEquals( "contents3", model3.getContents() ); |
| 118 | } |
| 119 | } |
Codes for com.developerscrappad.itest.CMPIntegrationTest.java
| Java(TM) 2 Platform Standard Edition 5.0 | | copy code | | ? |
| 001 | package com.developerscrappad.itest; |
| 002 | |
| 003 | import com.developerscrappad.entity.AppTable1Model; |
| 004 | import com.developerscrappad.entity.AppTable2Model; |
| 005 | import com.developerscrappad.entity.AppTable3Model; |
| 006 | import com.developerscrappad.intf.ICMPFacadeRemote; |
| 007 | import java.util.Properties; |
| 008 | import javax.naming.Context; |
| 009 | import javax.naming.InitialContext; |
| 010 | import org.junit.After; |
| 011 | import org.junit.Before; |
| 012 | import org.junit.Test; |
| 013 | import static org.junit.Assert.*; |
| 014 | |
| 015 | public class CMPIntegrationTest { |
| 016 | |
| 017 | private Properties env; |
| 018 | private Context ctx; |
| 019 | |
| 020 | public CMPIntegrationTest() { |
| 021 | env = new Properties(); |
| 022 | env.setProperty( "java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory" ); |
| 023 | env.setProperty( "java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces" ); |
| 024 | env.setProperty( Context.PROVIDER_URL, "jnp://localhost:1099" ); |
| 025 | } |
| 026 | |
| 027 | @Before |
| 028 | public void setUp() throws Exception { |
| 029 | ctx = new InitialContext( env ); |
| 030 | } |
| 031 | |
| 032 | @After |
| 033 | public void tearDown() throws Exception { |
| 034 | ICMPFacadeRemote cmpFacade = ( ICMPFacadeRemote ) ctx.lookup( ICMPFacadeRemote.JNDI_NAME ); |
| 035 | cmpFacade.cleanupTables(); |
| 036 | } |
| 037 | |
| 038 | @Test |
| 039 | public void testCMPProcessCommit() throws Exception { |
| 040 | AppTable1Model model1 = new AppTable1Model(); |
| 041 | model1.setContents( "contents1" ); |
| 042 | |
| 043 | AppTable2Model model2 = new AppTable2Model(); |
| 044 | model2.setContents( "contents2" ); |
| 045 | |
| 046 | AppTable3Model model3 = new AppTable3Model(); |
| 047 | model3.setContents( "contents3" ); |
| 048 | |
| 049 | ICMPFacadeRemote cmpFacade = ( ICMPFacadeRemote ) ctx.lookup( ICMPFacadeRemote.JNDI_NAME ); |
| 050 | cmpFacade.executeCMPProcess( model1, model2, model3 ); |
| 051 | |
| 052 | model1 = cmpFacade.findAppTable1ByContents( "contents1" ); |
| 053 | assertEquals( "contents1", model1.getContents() ); |
| 054 | |
| 055 | model2 = cmpFacade.findAppTable2ByContents( "contents2" ); |
| 056 | assertEquals( "contents2", model2.getContents() ); |
| 057 | |
| 058 | model3 = cmpFacade.findAppTable3ByContents( "contents3" ); |
| 059 | assertEquals( "contents3", model3.getContents() ); |
| 060 | } |
| 061 | |
| 062 | @Test |
| 063 | public void testCMPProcessRollback() throws Exception { |
| 064 | AppTable1Model model1 = new AppTable1Model(); |
| 065 | model1.setContents( "contents1" ); |
| 066 | |
| 067 | AppTable2Model model2 = new AppTable2Model(); |
| 068 | model2.setContents( "contents2" ); |
| 069 | |
| 070 | AppTable3Model model3 = new AppTable3Model(); |
| 071 | model3.setContents( "contents3" ); |
| 072 | |
| 073 | ICMPFacadeRemote cmpFacade = ( ICMPFacadeRemote ) ctx.lookup( ICMPFacadeRemote.JNDI_NAME ); |
| 074 | cmpFacade.executeCMPProcess( model1, model2, model3 ); |
| 075 | |
| 076 | model1 = new AppTable1Model(); |
| 077 | model1.setContents( "contents1.1" ); |
| 078 | |
| 079 | model2 = new AppTable2Model(); |
| 080 | model2.setContents( "contents2.2" ); |
| 081 | |
| 082 | model3 = new AppTable3Model(); |
| 083 | model3.setContents( "contents3" ); |
| 084 | |
| 085 | try { |
| 086 | cmpFacade.executeCMPProcess( model1, model2, model3 ); |
| 087 | fail(); |
| 088 | } catch ( Exception ex ) { |
| 089 | //absorb |
| 090 | } |
| 091 | |
| 092 | try { |
| 093 | model1 = cmpFacade.findAppTable1ByContents( "contents1.1" ); |
| 094 | fail(); |
| 095 | } catch ( Exception ex ) { |
| 096 | //absorb |
| 097 | } |
| 098 | |
| 099 | model1 = cmpFacade.findAppTable1ByContents( "contents1" ); |
| 100 | assertEquals( "contents1", model1.getContents() ); |
| 101 | |
| 102 | try { |
| 103 | model2 = cmpFacade.findAppTable2ByContents( "contents2.2" ); |
| 104 | fail(); |
| 105 | } catch ( Exception ex ) { |
| 106 | //absorb |
| 107 | } |
| 108 | |
| 109 | model2 = cmpFacade.findAppTable2ByContents( "contents2" ); |
| 110 | assertEquals( "contents2", model2.getContents() ); |
| 111 | |
| 112 | model3 = cmpFacade.findAppTable3ByContents( "contents3" ); |
| 113 | assertEquals( "contents3", model3.getContents() ); |
| 114 | } |
| 115 | } |
Codes for com.developerscrappad.itest.MDBIntegrationTest
| XML | | copy code | | ? |
| 001 | package com.developerscrappad.itest; |
| 002 | |
| 003 | import com.developerscrappad.intf.IBMPFacadeRemote; |
| 004 | import com.developerscrappad.intf.ICMPFacadeRemote; |
| 005 | import java.util.Properties; |
| 006 | import java.util.logging.Level; |
| 007 | import java.util.logging.Logger; |
| 008 | import javax.jms.Connection; |
| 009 | import javax.jms.ConnectionFactory; |
| 010 | import javax.jms.Destination; |
| 011 | import javax.jms.JMSException; |
| 012 | import javax.jms.Message; |
| 013 | import javax.jms.MessageProducer; |
| 014 | import javax.jms.Session; |
| 015 | import javax.jms.TextMessage; |
| 016 | import javax.naming.Context; |
| 017 | import javax.naming.InitialContext; |
| 018 | import javax.naming.NamingException; |
| 019 | import org.junit.After; |
| 020 | import org.junit.AfterClass; |
| 021 | import org.junit.Before; |
| 022 | import org.junit.BeforeClass; |
| 023 | import org.junit.Test; |
| 024 | import static org.junit.Assert.*; |
| 025 | |
| 026 | public class MDBIntegrationTest { |
| 027 | |
| 028 | private Properties env; |
| 029 | private Context ctx; |
| 030 | |
| 031 | public MDBIntegrationTest() { |
| 032 | env = new Properties(); |
| 033 | env.setProperty( "java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory" ); |
| 034 | env.setProperty( "java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces" ); |
| 035 | env.setProperty( Context.PROVIDER_URL, "jnp://localhost:1099" ); |
| 036 | } |
| 037 | |
| 038 | @BeforeClass |
| 039 | public static void setUpClass() { |
| 040 | } |
| 041 | |
| 042 | @AfterClass |
| 043 | public static void tearDownClass() { |
| 044 | } |
| 045 | |
| 046 | @Before |
| 047 | public void setUp() throws Exception { |
| 048 | ctx = new InitialContext( env ); |
| 049 | } |
| 050 | |
| 051 | @After |
| 052 | public void tearDown() throws Exception { |
| 053 | IBMPFacadeRemote bmpFacade = ( IBMPFacadeRemote ) ctx.lookup( IBMPFacadeRemote.JNDI_NAME ); |
| 054 | bmpFacade.cleanupTables(); |
| 055 | } |
| 056 | |
| 057 | @Test |
| 058 | public void testMDB() { |
| 059 | try { |
| 060 | sendJMSMessageToQueue( "mdbcontents" ); |
| 061 | Thread.sleep( 10000 ); |
| 062 | |
| 063 | ICMPFacadeRemote cmpFacade = ( ICMPFacadeRemote ) ctx.lookup( ICMPFacadeRemote.JNDI_NAME ); |
| 064 | |
| 065 | try { |
| 066 | cmpFacade.findAppTable1ByContents( "Hello mdbcontents" ); |
| 067 | } catch ( Exception ex ) { |
| 068 | fail(); |
| 069 | } |
| 070 | } catch ( Exception ex ) { |
| 071 | ex.printStackTrace(); |
| 072 | fail(); |
| 073 | } |
| 074 | } |
| 075 | |
| 076 | private Message createJMSMessageForjmsQueue( Session session, String messageData ) throws JMSException { |
| 077 | // TODO create and populate message to send |
| 078 | TextMessage tm = session.createTextMessage(); |
| 079 | tm.setText( messageData ); |
| 080 | |
| 081 | return tm; |
| 082 | } |
| 083 | |
| 084 | private void sendJMSMessageToQueue( String messageData ) throws NamingException, JMSException { |
| 085 | ConnectionFactory cf = ( ConnectionFactory ) ctx.lookup( "jms/TestQueueConnectionFactory" ); |
| 086 | Connection conn = null; |
| 087 | Session s = null; |
| 088 | |
| 089 | try { |
| 090 | conn = cf.createConnection(); |
| 091 | s = conn.createSession( false, s.AUTO_ACKNOWLEDGE ); |
| 092 | Destination destination = ( Destination ) ctx.lookup( "jms/TestQueue" ); |
| 093 | MessageProducer mp = s.createProducer( destination ); |
| 094 | mp.send( createJMSMessageForjmsQueue( s, messageData ) ); |
| 095 | } finally { |
| 096 | if ( s != null ) { |
| 097 | try { |
| 098 | s.close(); |
| 099 | } catch ( JMSException e ) { |
| 100 | Logger.getLogger( this.getClass().getName() ).log( Level.WARNING, "Cannot close session", e ); |
| 101 | } |
| 102 | } |
| 103 | if ( conn != null ) { |
| 104 | conn.close(); |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | } |
Making Things Happen Step-By-Step
We will begin the Step 1 by starting the server instance. This applies to both JBoss AS 5.x and JBoss AS 6.x. If you study the JBoss-Maven-Plugin’s functionality, it has capabilities to configure the server instance, but as I have mentioned earlier, it is better for us to create and configure the server instance manually instead of depending on the plugin. The plugin now is to use just for deployment and undeployment on the server. Just following through the below to deploy the EAR.
** Multiple terminal encouraged…
Step 1: Starting the Server Instance/Domain
The JBoss-Maven-Plugin allows you to start the server instace by executing “mvn jboss:start” within the DummyDemo-ear module. But, I will not excourage that as we can’t possibly see the output traces of the server log, so we’ll start the server instance/domain through the command prompt from <JBOSS_HOME> itself.
- Change the working directory to <JBOSS_HOME>/bin/.
- To start the server instance, execute “run -c dummyserver-standard“.
- Just wait for the server to be started and correct any startup configuration error if you encounter any.
Step 2: Deploying the EAR File to the JBoss Application Server
This is a very “sensitive” step and the failure rate is usually high. If anything goes wrong, I would highly suggest that you revisit all procedures mentioned above or back track Part 1 and Part 2 of the series. Anyway, to deploy the EAR file, follow the steps below:
Full Deployment Including Running The Integration Test
- Please change the working directory to <path>/DummyDemoJavaEE5/.
- Execute the “mvn install” command. This command will start the JBoss domain (if it is not started) and it will deploy the EAR to the application server, and then, it will run the integration tests within the integration-test module.
- Just wait for Maven to download the dependencies through the internet (may take a long time…). But if things broke down due to network failure or if you have difficult resolving dependencies, I’ll use the “-U” flag to perform this again, e.g. “mvn install -U“
- If it deploys properly, you should be able to see the below in the terminal:
... [INFO] Reactor Summary: [INFO] [INFO] DummyDemoJavaEE5 .................................. SUCCESS [0.090s] [INFO] project ........................................... SUCCESS [0.006s] [INFO] DummyDemo-api ..................................... SUCCESS [0.023s] [INFO] DummyDemo-ejb ..................................... SUCCESS [0.033s] [INFO] DummyDemo-web ..................................... SUCCESS [0.035s] [INFO] DummyDemo-appclient ............................... SUCCESS [0.052s] [INFO] DummyDemo-ear ..................................... SUCCESS [3.529s] [INFO] integration-test .................................. SUCCESS [0.018s] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ ...
If you have successfully completed this step, CONGRATULATIONS, your EAR file had been deployed and tested!
For Development Deployment (Without Running The Integration Test)
During development, we don’t need to always run the integration test for every single deployment, to achieve this while at your development with the JBoss App Server running, just perform the following:
- Change the working directory to <path>/DummyDemoJavaEE5/project/.
- For first time deployment of the EAR, just execute the command “mvn package” or “mvn install“.
Step 3: How to Undeploy or Redeploy the EAR to JBoss AS
In the midst of development, there will always be time when we need to redeploy the application again and again. To do that, just execute this at the <path>/DummyDemoJavaEE5/project/ working directory:
To UN-DEPLOY:
Just use the command: “mvn clean“. Since the the “undeploy” plugin goal was tagged with “clean” build-phase (just check on <path>/DummyDemoJavaEE5/project/DummyDemo-ear/pom.xml), when ever you execute the “clean” build cycle, it will perform the undeployment.
** Please take note that this command will fail if there is nothing to be undeploy on the JBoss App Server.
To RE-DEPLOY (only when there is an existing deployment in the JBoss app server):
Execute “mvn clean install” or “mvn clean package” at the terminal.
Summary on Part 4
Deployment to JBoss AS 5.x and JBoss AS 6.x through the JBoss-Maven-Plugin is pretty straight forward. The only thing that might caused annoyance is the long download time of dependency libraries when using the jboss-as-client for integration test. Overall, just make sure you have the right repository URLs, the right pom.xml in the DummyDemo-ear module and the integration-test module, you should do fine.
To Be Continue…
From here, I’ll be working on the Maven deployment on JBoss AS 7.x or Weblogic, which ever one comes first. So, stay tune.
Proceed To Part 5:
Related Articles:
- Building and Deploying Java EE EAR with Maven to Java EE Application Server (Part 1) – Project Directory Structure & Module Generation Through archetype:generate
- Building and Deploying Java EE EAR with Maven to Java EE Application Server (Part 2) – Where to put your source codes and pom.xml (EJB, MDB, Web & Enterprise Application Client)
- Building and Deploying Java EE EAR with Maven to Java EE Application Server (Part 3) – Deployment To Glassfish 3.x
- Building and Deploying Java EE EAR with Maven to Java EE Application Server (Part 5) – Deployment To Weblogic 10.3.x & Weblogic 12.x


Pingback: Building and Deploying Java EE EAR with Maven to Java EE Application Server (Part 3) – Deployment To Glassfish 3.x – A Developer’s ScrapPad | A Developer's Scrappad