Wednesday, May 18, 2011

Ruby on Rails vs Java ORM

Wow, I am having fun with Ruby on Rails. The migrations and scaffolding alone make me less of a coder and more of an Engineer. Lol, actually, it is kind of like that movie Wall-E. You know... where all the humans relied on the robots to do everything, evolving the humans into weak boned blobs. Rails does a lot for you, and it is hard for a Java programmer, like myself, to give up the deep control I had over the software. Or is it???

One of the concepts I learned today was the use of named scopes in Rails. The concept is that you can predefine a db query, which can be dynamically added to any normal method call on the Model. For example, lets say you have 2 tables, Customer, and Address. A Customer can have one or many Address records. So, I want to capture that in my Customer model. The default functionality for the Customer model would be to return one, or many, Customer records, using the find() method. Which is good, but I also want to return the Address information as well. Consider the following Customer model:
class Customer < ActiveRecord::Base
has_many :addresses, :dependent > :delete_all, :foreign_key => 'person_id'
named_scope :with_address_info, {
:select => "customers.*, addresses.*",
:joins => "JOIN addresses on addresses.person_id = customers.id",
:order => "customers.last_name asc" }
end
The above code is all I need for a simple Customer model, customer.rb. The has_many identifier tells Ruby that the Customer can have many Address records. The "with_address_info" named scope allows me to dynamically include the Address records with any query. In my Controller, I can have the following code:
customers = Customer.find(:all)
customerFull = Customer.with_address_info.find(:all)
That's all. Pretty powerful stuff. That small model file has given me the ability to Create, Retrieve, Update, and Delete (CRUD), all in 7 lines of code. Technically, if I don't care about the named scope, the size of the file would be 3 lines of code. But that seems pretty scary. It is like magic. Obviously, it isn't magic. The Rails framework writes all of those extra lines of code for you behind the scenes.

So, does it really take away control from a programmer, more than Java? Well, if you are use to manually creating JDBC calls to a database, then YES. You have a lot more control with the DB access in the code itself when you work with the straight JDBC drivers.
But, the need for that level of control is rare. Many of us use Object Relational Mapping (ORM), like Hibernate or Java Persistence API (JPA). In that case, Java takes the JDBC overhead away as well as the Rails framework. Of course, the syntax between Ruby and Java is such that a Ruby Model is much smaller in lines of code then a JPA entity (all those getter and setters add up). There is also the dynamic typing that I won't get into. But if you consider the "magic" with mapping a software object to a database object, then they are performing the same actions. Consider the following JPA Entity:

import java.io.Serializable;
import javax.persistence.*;
import java.util.Set;
@Entity
@Table(name="customers")
public class Customer implements Serializable {
@Id
private int id;
@Column(name="customer_type")
private String customerType;
@Column(name="email_address")
private String emailAddress;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;
@OneToMany(mappedBy="customer")
private Set<Address> addresses;
public Customer() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getCustomerType() {
return this.customerType;
}
public void setCustomerType(String customerType) {
this.customerType = customerType;
}
public String getEmailAddress() {
return this.emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Set<Address> getAddresses() {
return this.addresses;
}
public void setAddresses(Set<Address> addresses) {
this.addresses = addresses;
}
}
The Customer entity is a lot more wordy then the Customer Rails Model, but it performs the same kind of magic, just a little differently. If you were to use this Entity in a session EJB, you could have the following code:
List customers = entityManager.createQuery(" from Customers c ");
This code already has a reference to the address records. You can just call the getAddresses() method:
for (Customer customer : customers) {
List<Address> addresses = customer.getAddresses();
....
}
So, overall, Ruby on Rails provides a quick, and less verbose method of getting data across multiple tables. But if you use some popular ORM packages, you really arn't losing the control you thought you had.

Saturday, January 01, 2011

Happy New Year

Well, 2010 is gone. I liked 2010. I've learned a lot and expanded into a new career. My new years resolution is to keep on learning, and keep up on the blog. Blogging is fun, but it is also a great way for me to keep my skills up. So, happy new year to everyone.  Keep coding.

Thursday, December 30, 2010

Java EE Packaging

During my off hours, I do a little teaching. The course I teach relates to Java EE and EJB development. Over the past 3 years that I've been teaching this course, I've realized there are quite a few common issues that students get hung up on. Initially, the issues with classpaths, packaging, and deployment are the biggest hurdles. So, here is an overview about the packaging of Java components. I just JBoss as an example, but most Java EE servers are handled in a similar way.

First, we have the Java ARchive, or jar, file. A jar file is one of the earliest package formats in Java, and can be used to compress, and package classes together in a neat library. In the EJB world, the jar is what you use to package your EJBs. You can package EJBs by themselves, and deploy them separately, or they can be included as an Enterprise application (more on that later). The jar file contains your classes, and a META-INF directory. The META-INF directory contains config and deployment descriptor files. To create a JAR file, just go to the top level of your class hierarchy and type "jar cvf jarFile.jar .". This will package your classes, properties, config, and deployment descriptor files into a single jar file. From here, you can deploy it straight to the Java EE server. It should be noted that some Java EE servers require some extra deployment descriptor files, refer to the vendor documentation for more information. However, in the case of JBoss, you can just copy the file into the "deploy" directory, and JBoss will run with it.

Upon deployment, JBoss will unpack the code, and set up the EJBs to be used. During the deployment, errors can happen. This is a good time to pay attention to the logs. Many problems happen with the deployment descriptors, or when you are using injection, with annotations. So, looking at the logs will help you to determine if the problems are runtime, or deployment time. A successful deployment would contain something similar to the following in your server.log, or standard output.
2010-12-29 10:01:08,666 INFO [org.jboss.ejb3.session.SessionSpecContainer] (HDScanner) Starting jar=MyTestEjbs.jar,name=HelloWorld,service=EJB3
2010-12-29 10:01:08,677 INFO [org.jboss.ejb3.EJBContainer] (HDScanner) STARTED EJB: awg.ejb.session.HelloWorldImpl ejbName: HelloWorld
2010-12-29 10:01:08,752 INFO [org.jboss.ejb3.proxy.impl.jndiregistrar.JndiSessionRegistrarBase] (HDScanner) Binding the following Entries in Global JNDI:
HelloWorld/remote - EJB3.x Default Remote Business Interface
HelloWorld/remote-awg.ejb.session.HelloWorldRemote - EJB3.x Remote Business Interface
The above log entry not only tells me that the ejb was deployed without problems, but it also give me some information about the ejb. For example, the JNDI name. In this case, the JNDI name is "HelloWorld/remote". This is very useful for when you are writing your client code.

Once the EJB is deployed successfully, you can use it in some client code. You can write a local client, or you can create a web based client. The important thing to remember when writing a client to access an ejb is that ejbs are considered remote objects. So, you need to supply some information in your code on where to find the references to the ejbs. This is commonly performed using the JNDI. If you are accessing the ejbs from a jsp, and it is deployed on the same server instance, then the access to the ejb is pretty simple. Just get a reference to the Initial Context of the JNDI, and pass in a String, for the name of the resource you are trying to get. For example, if we want a reference to the HelloWorld ejb I deployed, I would use the following code:
InitialContext ctx = new InitialContext();
awg.ejb.session.HelloWorldRemote hwr = (awg.ejb.session.HelloWorldRemote) ctx.lookup("HelloWorld/remote");
However, if we are using a client remotely, we need to write code that is more involved. For example, consider the following command line client:
package awg.client;

import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import awg.ejb.session.HelloWorldRemote;

public class CommandLineHelloEJBClient {

 public static void main(String[] args) {
   try {
     // create an InitialContext, with JNDI properties
     Properties p = new Properties();
     p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
     p.put(Context.PROVIDER_URL, "jnp://localhost:1099");
     Context ic = new InitialContext(p);

     // Get the a reference to the Remote Object from JNDI
     HelloWorldRemote client = (HelloWorldRemote)ic.lookup("HelloWorld/remote");

     // run methods on the ejb
     client.....();

   } catch (Exception e) {
     e.printStackTrace();
   }
  }
}
In the above example, I am giving the URL, jnp://localhost:1099, and the Context type.

With local clients, we can wrap them in jar files as well and run them from the local command line. But what about web resources, like JSPs? In the Java world, web resources can be packaged up in their own package as well. The Web ARchive, or war file. The WAR follows a specific directory structure to allow for easy deployment of web resources.

The top level directory is considered the webroot, and it contains JSPs, HTML, CSS, JavaScript files, images, and any other file you want to be exposed to the web. Within the webroot, there is also a WEB-INF directory. Inside the WEB-INF directory, config files, and deployment descriptors, are kept. Also in the WEB-INF directory, there is a directory for your classes, and a lib directory, for any libraries your'd like to package with your web application. To create a war file, just go to the webroot directory and type "jar cvf warFile.war .". This will package your web files, and additional classes and jars into a war file. From here, you can deploy it your Java EE server, just like you did with the ejb jar file.

The jar and war files are technically all you need to run your web files, and ejbs. But to make the deploying and submitting even easier, and cleaner, you can use an Enterprise ARchive, or ear, file.
The ear file is nothing more than a single package containing your war file(s), jar file(s), and deployment descriptors. To build an ear file, follow these steps:
  1. Package up your ejbs into a jar file
  2. Package up your web resources into a war file
  3. Throw your war and jar files into a directory where you can package them together
  4. Create a new directory called META-INF (under the directory containing your packages)
  5. In the META-INF directory, create a deployment descriptor called application.xml.
    * The application.xml file defines the modules in your application.
  6. From the directory containing your packages, package everything up as an ear file: jar cvf MyApplication.ear .
  7. Deploy the ear to the JBoss deploy directory.
Seems like a lot of work, however, you can build Ant script, or use an IDE like Eclipse or Net Beans to do all of the heavy lifting. I'll talk more about that in another post.

The ear file makes deployment much more organized, and testing of the module is simpler. One thing to remember is that the Context lookup will be different with an ear, compared to using a war and jar separately. The ear context is basically the name of your ear file. For your lookup, you need to reference the ear context, when using JBoss. For example:
  • I created an ear called MyApplication.ear. The file contains all of the examples from the class. changed all of the lookups to include "MyApplication/" at the beginning of the look up. If I deployed the war and jar files separate, the code in the HelloWorld client would look like this:
    HelloWorldRemote hwr = (HelloWorldRemote) ctx.lookup("HelloWorld/remote");
  • However, since I am deploying it as an ear file, the code now references the ear context:
    HelloWorldRemote hwr = (HelloWorldRemote) ctx.lookup("MyApplication/HelloWorld/remote");
It should be noted that the JNDI names are not standard. Each Java EE server has it's own way for referencing ejbs on the JNDI. You may need to refer to documentation to get the format of the name. You can also use the ejb-jar.xml file in the META-INF directory of you ejb package, to customize the name, but there are still vendor specific formats tied in. That is why they are called by string names. If you write your code correctly, you can hold the names in a properties file, XML, or even a database. This will keep your code more portable. However, in EJB 3.1, JNDI names will be global and will follow a standard pattern that will make your code more portable across different application servers. But that is another story.

For more information, you can refer to the JBoss "Getting Started" documentation: https://www.jboss.org/file-access/default/members/jbossas/freezone/docs/Getting_Started_Guide/beta500/html-single/index.html#Configuration_Files