**TODO** detail the customizations to the Andromda files to support the connection to the Glassfish container from a JUnit test.
Add the following to the ServiceDelegate.vsl file for Andromda.
Properties prop = new Properties();
prop.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.impl.SerialInitContextFactory");
prop.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
prop.setProperty("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
this.setProperties(prop);
Friday, September 28, 2012
Thursday, September 27, 2012
AndroMDA 3.4 and Glassfish EJB3 persistence layer
After long wait AndroMDA 3.4 has finally been officially released. The build seems extremely stable and an excellent product. Congratulations and many thanks to the AndroMDA team.
Information on using AndroMDA can be found at: www.andromda.org *Warning* the last time I checked all of the documentation found on that main site was outdated. The tutorials would not work exactly as described. I have not checked to see if this has been updated since. I always get my information from the forums, which are active and the Andromda team is great about answering questions posted there, and keeping the community up to date with whatever is happening with AndroMDA.
I have been using AndroMDA for code generation of the middle tier/backend code for projects for the past 4 years. The environment I target is Glassfish with MySQL or PostGres database. EJB3/Hibernate persistence, and generally will enable webservices (jsr-181) as well.
AndroMDA projects will *not* deploy properly to Glassfish from initial generation and will require some massaging. (The default platform for AndroMDA ejb3 projects is JBoss.)
Step 1 - Generate blank project.
From a command prompt issue the statement: mvn org.andromda.maven.plugins:andromdapp-maven-plugin:3.4:generate Run through the various questions. Select EJB3/JPA for the persistence layer and Hibernate. Select uml2 for the uml format. This will create a blank project directory with the maven scripts and a blank UML diagram template under the MDA folder. If you attempt to compile this at this point, it will fail. There are missing versions for a couple of the dependencies in the core/pom.xml folder.
Step 2 - fix core/pom.xml dependency issue open the pom.xml file located under the core folder.
Search for the dependency sections for the slf4j-api and slf4j-log2j12. Comment these two sections out.
Step 3 - Compile project.
From the root of the newly created project folder run the command: mvn This will compile the project and in the process bring down some additional artifacts, namely the andromda profiles needed for the UML modelling portion.
Step 4- Open blank UML project using MagicDraw or other compatible UML tool.
Open the blank uml project created by Andromda. This will be located under the mda/src/main/uml folder.
Step 5 - Point MagicDraw to the Andromda profiles
This will fail the first time you open the model if you have not defined the location of your maven2 repository. In magicdraw, under the Options-Environment-Path Variables menu be sure to modify (or add) the variable: maven2.repository and point it to your .m2/repository directory (wherever you created it with maven2. Mine is under c:/users/myname/.m2/repository
Step 6 - Create a basic UML class diagram, and annotate classes.
Create a new class diagram, and add a new class.. annotate the class with the Entity stereotype (make sure you select the andromda entity, not the uml2 one...)
Step 7 - Export UML2 diagram
File-Save the updated model, and then File-Export-UML2 the model.
Step 8 - Compile project
From a command prompt type mvn This will generate code, but will likely fail with additional issues due to the generation of files needed for JBoss, but unnecessary for a Glassfish deployment.
Step 9 - Fix EJBContainer dependency issues
Step 10 - Import into Eclipse
Step 11 - Create blank MySQL schema
Step 12 - Add jdbc datasource to Glassfish
Step 13 - Modify default pom.xml
The naming convention for the jdbc datasources used by JBoss is different than glassfish. In the main pom.xml located in the root of your project, modify the following section as shown:
Step 14 - Add mergeLocation property to the EJB3 section of andromda.xml file
**Also need to change the persistenceProvider to glassfish in here...
Step 15 - Add customized files to custom/ejb3 folder
Step 15a) - ejb-jar.vsl
Step 15b) - cartridge.xml remove ejbcontainer and other classes.
The SessionLocal, SessionRemote, SessionInterface,ServiceDelegate,ServiceDelegateBase and ServiceLocator is genereted in the wrong jar by default... it needs to be generated in the commons package, not the core. The cartridge.xml file cannot be replaced in AndroMDA the same way the .vsl template files can, but AndroMDA does provide a 'mergeMapping' mechanism which can be used to modify the andromda.xml file. To make the necessary changes, create a new file called EJB3MergeMappings.xml and place it in the folder: %YourProjectFolder%\mda\src\main\config\mappings folder. This file should contain the below code (approximately...cut the sections from the cartridge.xml from the andromda ejb3 cartridge jar located in your maven repository to ensure you have the exact sections..also... the below does not contain all of the sections...). In your andromda.xml file, uncomment the line:
Note: You will also have to modify your common/pom.xml to add the ejb jar dependency or the interfaces will not compile properly...
Step 1 - Generate blank project.
From a command prompt issue the statement: mvn org.andromda.maven.plugins:andromdapp-maven-plugin:3.4:generate Run through the various questions. Select EJB3/JPA for the persistence layer and Hibernate. Select uml2 for the uml format. This will create a blank project directory with the maven scripts and a blank UML diagram template under the MDA folder. If you attempt to compile this at this point, it will fail. There are missing versions for a couple of the dependencies in the core/pom.xml folder.
Step 2 - fix core/pom.xml dependency issue open the pom.xml file located under the core folder.
Search for the dependency sections for the slf4j-api and slf4j-log2j12. Comment these two sections out.
Step 3 - Compile project.
From the root of the newly created project folder run the command: mvn This will compile the project and in the process bring down some additional artifacts, namely the andromda profiles needed for the UML modelling portion.
Step 4- Open blank UML project using MagicDraw or other compatible UML tool.
Open the blank uml project created by Andromda. This will be located under the mda/src/main/uml folder.
Step 5 - Point MagicDraw to the Andromda profiles
This will fail the first time you open the model if you have not defined the location of your maven2 repository. In magicdraw, under the Options-Environment-Path Variables menu be sure to modify (or add) the variable: maven2.repository and point it to your .m2/repository directory (wherever you created it with maven2. Mine is under c:/users/myname/.m2/repository
Step 6 - Create a basic UML class diagram, and annotate classes.
Create a new class diagram, and add a new class.. annotate the class with the Entity stereotype (make sure you select the andromda entity, not the uml2 one...)
Step 7 - Export UML2 diagram
File-Save the updated model, and then File-Export-UML2 the model.
Step 8 - Compile project
From a command prompt type mvn This will generate code, but will likely fail with additional issues due to the generation of files needed for JBoss, but unnecessary for a Glassfish deployment.
Step 9 - Fix EJBContainer dependency issues
Step 10 - Import into Eclipse
Step 11 - Create blank MySQL schema
Step 12 - Add jdbc datasource to Glassfish
Step 13 - Modify default pom.xml
The naming convention for the jdbc datasources used by JBoss is different than glassfish. In the main pom.xml located in the root of your project, modify the following section as shown:
<dataSource.name>${application.id}</dataSource.name>
<dataSource>jdbc/${dataSource.name}</dataSource>
*** also need to remove the 'provided' scope for commons-collections. Find the following section:
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
<!-- JBoss contains a version which causes a classloader conflict unless not bundled -->
<scope>provided</scope>
</dependency>
Remove the 'provided' scope. (you can delete that line)Step 14 - Add mergeLocation property to the EJB3 section of andromda.xml file
**Also need to change the persistenceProvider to glassfish in here...
Step 15 - Add customized files to custom/ejb3 folder
Step 15a) - ejb-jar.vsl
Step 15b) - cartridge.xml remove ejbcontainer and other classes.
The SessionLocal, SessionRemote, SessionInterface,ServiceDelegate,ServiceDelegateBase and ServiceLocator is genereted in the wrong jar by default... it needs to be generated in the commons package, not the core. The cartridge.xml file cannot be replaced in AndroMDA the same way the .vsl template files can, but AndroMDA does provide a 'mergeMapping' mechanism which can be used to modify the andromda.xml file. To make the necessary changes, create a new file called EJB3MergeMappings.xml and place it in the folder: %YourProjectFolder%\mda\src\main\config\mappings folder. This file should contain the below code (approximately...cut the sections from the cartridge.xml from the andromda ejb3 cartridge jar located in your maven repository to ensure you have the exact sections..also... the below does not contain all of the sections...). In your andromda.xml file, uncomment the line:
file:${conf.dir}/mappings/EJB3MergeMappings.xml
Note: You will also have to modify your common/pom.xml to add the ejb jar dependency or the interfaces will not compile properly...
<mappings name="EJB3MergeMappings">
<mapping>
<from><![CDATA[<template
path="templates/ejb3/ServiceDelegate.vsl"
outputPattern="$generatedFile"
outlet="services"
overwrite="true">
<modelElements variable="service">
<modelElement>
<type name="org.andromda.cartridges.ejb3.metafacades.EJB3SessionFacade"/>
</modelElement>
</modelElements>
</template>]]></from>
<to>
<![CDATA[<template
path="templates/ejb3/ServiceDelegate.vsl"
outputPattern="$generatedFile"
outlet="commons"
overwrite="true">
<modelElements variable="service">
<modelElement>
<type name="org.andromda.cartridges.ejb3.metafacades.EJB3SessionFacade"/>
</modelElement>
</modelElements>
</template>]]>
</to>
</mapping>
<mapping>
<from><![CDATA[<template
path="templates/ejb3/ServiceDelegateBase.vsl"
outputPattern="$generatedFile"
outlet="services"
overwrite="true"
outputToSingleFile="true"
outputOnEmptyElements="false">
<modelElements>
<modelElement variable="services">
<type name="org.andromda.cartridges.ejb3.metafacades.EJB3SessionFacade"/>
</modelElement>
</modelElements>
</template>]]></from>
<to>
<![CDATA[<template
path="templates/ejb3/ServiceDelegateBase.vsl"
outputPattern="$generatedFile"
outlet="commons"
overwrite="true"
outputToSingleFile="true"
outputOnEmptyElements="false">
<modelElements>
<modelElement variable="services">
<type name="org.andromda.cartridges.ejb3.metafacades.EJB3SessionFacade"/>
</modelElement>
</modelElements>
</template>]]>
</to>
</mapping>
<mapping>
<from><![CDATA[<template
path="templates/ejb3/ServiceLocator.vsl"
outputPattern="$generatedFile"
outlet="services"
overwrite="true"
outputToSingleFile="true"
outputOnEmptyElements="false">
<modelElements>
<modelElement variable="services">
<type name="org.andromda.cartridges.ejb3.metafacades.EJB3SessionFacade"/>
</modelElement>
</modelElements>
</template>]]></from>
<to>
<![CDATA[<template
path="templates/ejb3/ServiceLocator.vsl"
outputPattern="$generatedFile"
outlet="commons"
overwrite="true"
outputToSingleFile="true"
outputOnEmptyElements="false">
<modelElements>
<modelElement variable="services">
<type name="org.andromda.cartridges.ejb3.metafacades.EJB3SessionFacade"/>
</modelElement>
</modelElements>
</template>]]>
</to>
</mapping>
<mapping>
<from><![CDATA[<template
path="templates/ejb3/SessionInterface.vsl"
outputPattern="$generatedFile"
outlet="session-beans"
overwrite="true"
generateEmptyFiles="true">
<modelElements variable="service">
<modelElement>
<type name="org.andromda.cartridges.ejb3.metafacades.EJB3SessionFacade"/>
</modelElement>
</modelElements>
</template>]]></from>
<to>
<![CDATA[<template
path="templates/ejb3/SessionInterface.vsl"
outputPattern="$generatedFile"
outlet="commons"
overwrite="true"
generateEmptyFiles="true">
<modelElements variable="service">
<modelElement>
<type name="org.andromda.cartridges.ejb3.metafacades.EJB3SessionFacade"/>
</modelElement>
</modelElements>
</template>]]>
</to>
</mapping>
<mapping>
<from><![CDATA[<template
path="templates/ejb3/SessionLocal.vsl"
outputPattern="$generatedFile"
outlet="session-beans"
overwrite="true"
generateEmptyFiles="true">
<modelElements variable="service">
<modelElement>
<type name="org.andromda.cartridges.ejb3.metafacades.EJB3SessionFacade">
<property name="viewTypeLocal"/>
</type>
</modelElement>
</modelElements>
</template>]]></from>
<to>
<![CDATA[<template
path="templates/ejb3/SessionLocal.vsl"
outputPattern="$generatedFile"
outlet="commons"
overwrite="true"
generateEmptyFiles="true">
<modelElements variable="service">
<modelElement>
<type name="org.andromda.cartridges.ejb3.metafacades.EJB3SessionFacade">
<property name="viewTypeLocal"/>
</type>
</modelElement>
</modelElements>
</template>]]>
</to>
</mapping>
</mappings>
Sunday, June 24, 2012
Using Andromda 3.4-SNAPSHOT to deploy to Glassfish
Glassfish requires a few modifications to the andromda files before it will deploy properly.
The ejb-jar.xml must be modified to change instances of and to and
The format of the jdbc jndi parameter in the main pom.xml must be modified. Here is how:
Modifying the ejb-jar.xml:
Generate a new andromda project. In the mda/src/main/config folder create the following directory structure: custom/ejb3/templates/ejb3/config folder. Create a new ejb-jar.xml.vsl file in this folder. Add the following to that folder:
<?xml version="1.0" encoding="${xmlEncoding}"?>
<ejb-jar
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/ejb-jar_3_0.xsd"
version="3.0">
<description><![CDATA[No Description.]]></description>
<display-name>Generated by AndroMDA EJB3 Cartridge</display-name>
<enterprise-beans>
#foreach ($service in $services)
<session>
<description>
<![CDATA[
$service.getDocumentation("", 64, false)
]]>
</description>
<ejb-name>${service.serviceName}</ejb-name>
##
## Only define the remote business interface if the bean is NOT a Seam component
##
#**##if ($service.viewTypeLocal)
<business-local>${service.fullyQualifiedServiceLocalInterfaceName}</business-local>
#**##end
#**##if ($service.viewTypeRemote && !$service.seamComponent)
<business-remote>${service.fullyQualifiedServiceRemoteInterfaceName}</business-remote>
#**##end
<ejb-class>${service.fullyQualifiedServiceName}</ejb-class>
<session-type>${service.type}</session-type>
#**##if ($service.transactionManagementBean)
<transaction-type>Bean</transaction-type>
#**##else
<transaction-type>Container</transaction-type>
#**##end
#**##foreach($envEntry in $service.getEnvironmentEntries(true))
<env-entry>
<env-entry-name>${envEntry.name}</env-entry-name>
#if ($envEntry.type.primitive)
#set ($typeName = ${envEntry.type.wrapperName})
#else
#set ($typeName = ${envEntry.type.fullyQualifiedName})
#end
<env-entry-type>$converter.getJavaLangTypeName($typeName)</env-entry-type>
<env-entry-value>${envEntry.defaultValue.replace('"','')}</env-entry-value>
</env-entry>
#**##end
</session>
#end
#foreach ($manageable in $manageables)
<session>
<description>
<![CDATA[
$manageable.getDocumentation("", 64, false)
]]>
</description>
<ejb-name>${manageable.manageableServiceName}</ejb-name>
<business-remote>${manageable.fullyQualifiedManageableServiceName}</business-remote>
<ejb-class>${manageable.fullyQualifiedManageableServiceBaseName}</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
#end
#foreach ($entity in $entities)
<session>
<description>
<![CDATA[
$entity.getDocumentation("", 64, false)
]]>
</description>
<ejb-name>${entity.daoName}</ejb-name>
<business-local>${entity.fullyQualifiedDaoName}</business-local>
<ejb-class>${entity.fullyQualifiedDaoImplementationName}</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
#end
</enterprise-beans>
#if (${seamEnabled} == 'true')
<interceptors>
<interceptor>
<interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>
</interceptor>
</interceptors>
#end
<assembly-descriptor>
#foreach ($interceptor in $interceptors)
##
## Default interceptors
##
#**##if ($interceptor.defaultInterceptor)
#* *##if (!$defaultInterceptorExists)
<interceptor-binding>
<ejb-name>*</ejb-name>
#* *##set ($defaultInterceptorExists = true)
#* *##end
<interceptor-class>${interceptor.fullyQualifiedName}</interceptor-class>
#**##end
#end
#if ($defaultInterceptorExists)
</interceptor-binding>
#end
#foreach ($service in $services)
##
## Service listener - lifecycle callbacks are defined as an interceptor
##
#**##set ($interceptors = $service.interceptorReferences)
#**##if ($collectionUtils.size($interceptors) >= 1 || $service.listenerEnabled)
<interceptor-binding>
<ejb-name>${service.serviceName}</ejb-name>
#* *##if ($service.listenerEnabled)
<interceptor-class>${service.fullyQualifiedServiceListenerName}</interceptor-class>
#* *##end
#* *##foreach ($interceptor in $interceptors)
<interceptor-class>${interceptor.fullyQualifiedName}</interceptor-class>
#* *##end
#* *##if ($service.excludeDefaultInterceptors)
<exclude-default-interceptors/>
#* *##end
</interceptor-binding>
#**##end
#end
##
## Define method level interceptors for session beans
##
#foreach ($service in $services)
#**##foreach ($operation in $service.businessOperations)
#* *##set ($interceptors = $operation.interceptorReferences)
#* *##if ($collectionUtils.size($interceptors) >= 1)
<interceptor-binding>
<ejb-name>${service.serviceName}</ejb-name>
#* *##foreach ($interceptor in $interceptors)
<interceptor-class>${interceptor.fullyQualifiedName}</interceptor-class>
#* *##end
<method>
<method-name>${operation.name}</method-name>
#* *##if (!$operation.arguments.empty)
<method-params>
#* *##foreach ($argument in $operation.arguments)
<method-param>${argument.type.fullyQualifiedName}</method-param>
#* *##end
</method-params>
#* *##end
</method>
#* *##if ($operation.excludeClassInterceptors)
<exclude-class-interceptors/>
#* *##end
#* *##if ($operation.excludeDefaultInterceptors)
<exclude-default-interceptors/>
#* *##end
</interceptor-binding>
#* *##end
#**##end
#end
#foreach ($mdb in $mdbs)
##
## Message-driven bean listener - lifecycle callbacks are defined as an interceptor
##
#**##set ($interceptors = $mdb.interceptorReferences)
#**##if ($collectionUtils.size($interceptors) >= 1 || $mdb.listenerEnabled)
<interceptor-binding>
<ejb-name>${mdb.messageDrivenName}</ejb-name>
#* *##if ($mdb.listenerEnabled)
<interceptor-class>${mdb.fullyQualifiedMessageDrivenListenerName}</interceptor-class>
#* *##end
#* *##foreach ($interceptor in $interceptors)
<interceptor-class>${interceptor.fullyQualifiedName}</interceptor-class>
#* *##end
#* *##if ($mdb.excludeDefaultInterceptors)
<exclude-default-interceptors/>
#* *##end
</interceptor-binding>
#**##end
#end
#if (${seamEnabled} == 'true')
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>
</interceptor-binding>
#end
</assembly-descriptor>
</ejb-jar>
Modify the file: /mda/src/main/config/andromda.xml in the ejb3 section add the following:
<property name="mergeLocation">${conf.dir}/custom/ejb3</property>
That will format the ejb deployment descriptor properly.
2 - modify the main pom.xml
Find and modify the following two parameters as shown below:
<dataSource.name>${application.id}</dataSource.name>
<dataSource>jdbc/${dataSource.name}</dataSource>
Now you should be able to successfully deploy your project to glassfish.
Do not forget to set up the proper jndi jdbc resources within the glassfish admin console. That is outside the scope of this blog.
Subscribe to:
Posts (Atom)