Friday, June 7, 2013

EJB, MDB, Rest, JPA, CDI... All the JavaEE fun with Maven, Scala, TomEE and an awesome IDE support

This post is long and with lots of pictures. So, before going ahead, check out what you are about to see:

  • Another JavaEE application with Scala and TomEE
  • How to use Intellij to run your Scala application with TomEE and JUnit
  • How to debug Scala code with break points
I have been re-factoring our three sample applications: PhotoDB, FaceID and Msglnk. Basically, I changed the programming language used by the back end of each one of them. I wanted to give back some space to Java and open an opportunity to my new love affair, Scala.

Do you remember what these applications are for? PhotoDB is our photo manager application; FaceID is our user management system. PhotoDB delegates the user management to FaceID, so it can focus on photo management tasks; Msglnk is the messaging system of our solution. It holds the JavaMail configurations. That's how the other two applications send and receive emails without having any JavaMail knowledge.

All three applications are dummy. The intent is to give examples of JavaEE with different programming languages and JavaScript frameworks running on top of our dear Apache TomEE. This is what we have now:

Application Back end Front end
PhotoDB Java Backbone.js
FaceID Groovy ExtJS
Msglnk Scala YUI

This blog post will show how to build, run and debug the Msglnk project with Intellij and the Scala plugin supported by Jetbrains. The next steps are platform independent. They assume that you have Java and Intellij installed in your computer. You don’t need to worry about downloading and installing Scala itself. The maven plugin (maven-scala-plugin) does it for you.

Download and build the application via IDE

First step: install the Intellij plugin for Scala. This plugin is not included in your Intellij during the installation process, but it is ridiculously easy to get it configured afterwards. This plugin makes Intellij Scala code aware.

Open Intellij and go to the Plugins configuration window.



Click on "Browse repositories…"



Filter the "scala" plugins and right-click the one supported by Jetbrains. Select "Download and install". That’s it. I told you it was easy!



Click on "Check out from Version Control" and select "Git".


The "Git Repository URL" is https://github.com/tveronezi/msglnk.git. Select the "Parent Directory" and press "Clone".

Click on the "Enable Type-aware highlighting" link.


Go to "Maven Projects" -> "msglnk (root)" -> "Lifecycle" and dbl-click "install". Be patient because it may take some time. When you run the "install" command for the first time, maven downloads all the dependencies of the project, including the latest snapshot version of the TomEE server.


Once you see the "BUILD SUCCESS" message, go to the "msglnk/msglnk-resources/target" directory, copy and unzip the "tomee-runtime.tar.gz" archive in a directory of tour choice. Please note that the "target" directories are maven temporary locations. The "target" directories will be deleted after a mvn clean call.


Replace the content of the tomcat-users.xml configuration file by the text below.

 <?xml version='1.0' encoding='utf-8'?>  
 <tomcat-users>  
  <role rolename="solution-admin" />  
  <user username="admin" password="admin" roles="solution-admin" />  
 </tomcat-users>  

Go to "Edit Configurations" -> "+" -> "TomEE Server" -> "Local"



Click "Configure" and say the location of your TomEE server.


Click "Fix" -> "msglnk-gui:war exploded"


Set the "Application context" to "/msglnk" and click ok


Preparing an email account

Msglnk manages emails. Therefore, it depends on an external POP/SMTP server in order to run the tests. It was designed to be used with gmail. Follow the official tutorial on how to enable POP and SMTP access to your gmail account (Available here). Once you have your gmail account ready, use the "msglnk/msglnk-web/src/test/resources/default-session.properties" file as template for the parameters of your account.

 ux_session_user_account=foo@bar.org  
 ux_session_user_password=foo_bar  
 mail.smtp.auth=true  
 mail.smtp.starttls.enable=true  
 mail.smtp.host=smtp.gmail.com  
 mail.smtp.port=587  
 mail.host=pop.gmail.com  
 mail.store.protocol=pop3s  
 mail.pop3s.auth=true  
 mail.pop3s.port=995  

Create a system variable called MSGLNK_MAIL_SESSION_CONFIG with the location of the file above. In my case (linux), it would be...

 export MSGLNK_MAIL_SESSION_CONFIG=/home/tveronezi/Documents/default-session.properties  

It is time to run the application

It is all set to finally have some fun! Select one of the JUnit test classes and click "Run".



Just like Java, you can put break points in your Scala code. Put a break point on it and click "Debug".



Evaluating the value of the variables

We are ready to run the TomEE server. Click "Run TomEE".


Enter your username and password.


Done.


Note that if you want to use some break points, you can also run the "Debug" version of it. Try it!

Some examples of Scala JavaEE code

Here is a list of examples of how a Scala JaveEE application would look like.

Entity Bean
 package msglnk.data  
   
 import scala.reflect.BeanProperty  
 import javax.persistence._  
   
 @Entity  
 @Table(uniqueConstraints = Array(new UniqueConstraint(columnNames = Array("session_name"))))  
 class MailSession extends BaseEntity {  
   
   @Column(name = "session_name")  
   @BeanProperty  
   var name: String = _  
   
   @Column(nullable = false)  
   @Lob  
   @BeanProperty  
   var config: String = _  
   
 }  
   

DTO
 package msglnk.dto  
   
 import javax.xml.bind.annotation.{XmlElement, XmlAccessorType, XmlAccessType, XmlRootElement}  
 import scala.Predef.String  
 import scala.reflect.BeanProperty  
   
 @XmlAccessorType(XmlAccessType.NONE)  
 @XmlRootElement  
 class EmailDto {  
   
   @XmlElement  
   @BeanProperty  
   var session: String = _  
   
   @XmlElement  
   @BeanProperty  
   var from: String = _  
   
   @XmlElement  
   @BeanProperty  
   var to: String = _  
   
   @XmlElement  
   @BeanProperty  
   var subject: String = _  
   
   @XmlElement  
   @BeanProperty  
   var text: String = _  
   
 }  

MDB
 package msglnk.mdb  
   
 import javax.ejb.{EJB, MessageDriven}  
 import org.slf4j.LoggerFactory  
 import javax.jms.{Message, MessageListener}  
 import msglnk.service.MailSessionService  
 import javax.annotation.security.RunAs  
   
 @MessageDriven(mappedName = "SendEmailQueue", messageListenerInterface = classOf[MessageListener])  
 @RunAs("solution-admin")  
 class SendEmailRequest extends MessageListener {  
   val LOG = LoggerFactory.getLogger(classOf[SendEmailRequest])  
   
   @EJB var mailSession: MailSessionService = _  
   
   def onMessage(message: Message) {  
     val sessionName = message.getStringProperty("sessionName")  
     val to = message.getStringProperty("to")  
     val subject = message.getStringProperty("subject")  
     val text = message.getStringProperty("text")  
     mailSession.sendMail(sessionName, to, subject, text)  
   }  
 }  

REST
 package msglnk.rest  
   
 import javax.ejb.Stateless  
 import javax.ws.rs.{POST, FormParam, Path, Produces}  
 import javax.inject.Inject  
 import msglnk.service.MailSessionService  
   
 @Path("/session")  
 @Produces(Array("application/json"))  
 @Stateless  
 class Session {  
   
   @Inject var mailService: MailSessionService = _  
   
   @POST  
   def save(@FormParam("name") name: String, @FormParam("config") config: String) {  
     mailService.saveSession(name, config)  
   }  
   
 }  

EJB
 package msglnk.service  
   
 import javax.ejb.Stateless  
 import javax.annotation.security.RolesAllowed  
 import javax.persistence.{NoResultException, Query, PersistenceContext, EntityManager}  
 import msglnk.data.BaseEntity  
 import collection.JavaConversions._  
   
 @Stateless  
 @RolesAllowed(Array("solution-admin"))  
 class BaseEAO {  
   
   @PersistenceContext(unitName = "mailPU")  
   var em: EntityManager = _  
   
   def findAll[T](cls: Class[T]): Set[T] = {  
     val queryStr = "SELECT e FROM %s e".format(cls.getName)  
     val query = em.createQuery(queryStr)  
     val list = query.getResultList.asInstanceOf[java.util.List[T]]  
     list.toSet  
   }  
   
   def findUniqueBy[T, E](cls: Class[T], name: String, value: E): Option[T] = {  
     val queryStr = "SELECT e FROM %s e WHERE e.%s = :pValue".format(cls.getName, name)  
     val query = em.createQuery(queryStr)  
     query.setParameter("pValue", value)  
     findUnique(cls, query)  
   }  
   
   def findById[T, E](cls: Class[T], value: E): Option[T] = {  
     val obj = em.find(cls, value)  
     if (obj == null) {  
       None  
     } else {  
       Some(cls.cast(obj))  
     }  
   }  
   
   def findUnique[T](cls: Class[T], query: Query): Option[T] = {  
     try {  
       Some(cls.cast(query.getSingleResult))  
     }  
     catch {  
       case nre: NoResultException => None  
     }  
   }  
   
   def create[T <: BaseEntity](bean: T): T = {  
     if (bean.getUid == null) {  
       em.persist(bean)  
       em.flush()  
     } else {  
       // We don't need to do anything.  
       // Changes made to the entity will be persisted once the transaction is committed.  
     }  
   
     bean  
   }  
   
 }  
   

JUnit class
 package msglnk.tests  
   
 import msglnk.data.MailSession  
 import msglnk.runners.AdminRunner  
 import msglnk.service.BaseEAO  
 import org.junit.Assert  
 import org.junit.Test  
 import javax.inject.Inject  
 import msglnk.BaseTest  
 import javax.ejb.{EJBTransactionRolledbackException, EJBException}  
   
 class MailSessionBeanTest extends BaseTest {  
   @Inject var adminRunner: AdminRunner = _  
   @Inject var baseEAO: BaseEAO = _  
   
   private def createSessionObj(name: String, config: String) = {  
     val bean: MailSession = new MailSession  
     bean.setName(name)  
     bean.setConfig(config)  
     bean  
   }  
   
   @Test  
   def name_should_be_unique() {  
     try {  
       adminRunner.run({  
         baseEAO.create(createSessionObj("session a", "aaaaa"))  
         baseEAO.create(createSessionObj("session a", "bbbbb"))  
       })  
       Assert.fail("Exception expected")  
     }  
     catch {  
       case e: EJBException => {  
         Assert.assertEquals(classOf[EJBTransactionRolledbackException], e.getCausedByException.getClass)  
       }  
       case _ => Assert.fail("EJBException expected")  
     }  
   }  
   
   @Test  
   def config_should_not_be_null() {  
     adminRunner.run({  
       baseEAO.create(createSessionObj("session not null", ""))  
     })  
     try {  
       adminRunner.run({  
         baseEAO.create(createSessionObj("session null", null))  
       })  
       Assert.fail("Exception expected")  
     }  
     catch {  
       case e: EJBException => {  
         Assert.assertEquals(classOf[EJBTransactionRolledbackException], e.getCausedByException.getClass)  
       }  
       case _ => Assert.fail("EJBException expected")  
     }  
   }  
   
 }  

Resources

http://tomee.apache.org/
Apache TomEE and security
http://www.scala-lang.org/
http://scala-tools.org/mvnsites/maven-scala-plugin/
http://www.jetbrains.com/idea/

Conclusion

You don't need to quit JavaEE if you want to give a try to another programming language. TomEE supports Java, Groovy and Scala. Instead of quitting JavaEE, give TomEE a try. You will like it! :)

No comments:

Post a Comment