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

Monday, December 27, 2010

Installing & Running JBoss in Ubuntu

Being mostly a Java guy, a couple of my favorite servers to work with are Tomcat and JBoss. While at work, it has been exclusively Tomcat, lately, I've been mostly interested in working with JBoss. For a couple years, I've had JBoss installed on my Windows machine. It is pretty easy,just unzip on your hard drive and execute the run script in the bin directory.

Since I installed Ubuntu, I've wanted to switch to using Linux as my JBoss OS. With Ubuntu, I could easily use the update manager, or apt-get jboss, to get it installed. However, it wouldn't give me the version I wanted. So, I figured I'd work this manually.

The first thing I do, is download a zip. A common place to put the software is /usr/local. So, I will download, and unzip the package there.
cd /usr/local
sudo wget http://sourceforge.net/projects/jboss/files/JBoss/JBoss-6.0.0.Final/jboss-as-distribution-6.0.0.Final.zip
sudo unzip jboss-as-distribution-6.0.0.Final.zip
The funny thing is that I can't seem to find a tarball for JBoss, so I just get the zip. Based on this, and the directory structure of JBoss, it seems that the development of JBoss was mostly geared towards Windows based servers. Regardless, it works great on Linux as well.
Now, at this point, you can just run JBoss out of the box, using the run.sh script in th ebin directory.
sudo /usr/local/jboss-6.0.0.Final/bin/run.sh
This will start up the server, and the terminal window would be the standard output. It that is all you need, then you can stop right here. But in general, there are a couple things to simplify the start up and shut down process. I like to control the server as a service, from the /etc/init.d directory. Within the bin directory, there is a startup script which will call the run.sh script as the jboss user. The script is written with redhat in mind, but is easily modified to run with Ubuntu. But first, lets create a jboss user and grant ownership to the jboss directory.
sudo useradd -s /bin/bash -d /home/jboss -p jboss
sudo chown -R jboss:jboss /usr/local/jboss-6.0.0.Final/
The useradd command will create the jboss user, with the /home/jboss directory as it's home, and the default shell is bash. This command will create the jboss user without a password. If you are like me, and like to make things as secure as possible, use sudo passwd jboss to create a password for the jboss user.

sudo ln -s jboss-6.0.0.Final/ jboss. The chown command grants ownership to the jboss user. The -R, after the chown command, does a recursive call to the sub directories.
Now that the jboss user is set up, copy the jboss_init_redhat.sh file into the /ect/init.d directory, and name it jboss.
sudo cp jboss-6.0.0.Final/bin/jboss_init_redhat.sh /etc/init.d/jboss
There are a couple changes we need to make with the file. first, it assumes that the JBoss home directory is /usr/local/jboss. By default, our command to unzip the package created the home as jboss-6.0.0.Final/, so we can either rename the directory, with the mv command, change the init file to reflect the real path, or create a symbolic link. I prefer the symbolic link, because we can keep the old packages as we download more, and if we need to revert, it is just a matter of modifying the symlink.
sudo ln -s jboss-6.0.0.Final ./jboss
The next change is in the file itself. Use VI and modify the JAVAPTH variable to the correct path of your java command. In my case, and most likely yours as well, the path is /usr/local/bin. If you don't know the path, just use the command "which java". Verify some of your other variables, just in case you need to make changes for your system.

At this point, you are all set to run it as a service. Use the following commands to start, stop, and restart the server.
/etc/init.d/jboss start
/etc/init.d/jboss stop
/etc/init.d/jboss restart
If you created your jboss user with a password, you will need to supply the password. The server will start up and the logs will be written to /usr/local/jboss-6.0.0.Final/server/default/log/server.log. If you'd like to have the server start at system start, then you can update the rc.d to include it as an auto start up.
sudo update-rc.d jboss defaults
I personally prefer to start it up as needed. Once started, you can deploy your jars, wars, and ears as you normally would, in the deploy directory of the server you are using. In this case, we are using the default server.

Update 12/29: I updated this post to reflect the GA release of JBoss 6.0.

Wednesday, December 22, 2010

Ubuntu Wubi vs Grub Upgrade

Back in my younger days, I really loved tinkering around with Linux. I wouldn't consider myself a Linux expert, but I was learning, and gained a lot of experience. I stated out using Red Hat, before Fedora, and played around with Gentoo, PHLAK, and even built a firewall using Coyote Linux installed on a Floppy disk. It was a great learning experience, and I made sure I used a computer which I didn't need anymore.

Then, I had kids. All of my free time was taken up by the family. I needed to prioritize my life a little stricter, and family, personal health, job, and relationships took priority over Linux. So, for the following 5 years, I was a Windows user. My job didn't require any Unix or Linux work, and being a Java programmer (for the most part), it didn't matter what OS I used.

Last summer, I decided to get back into it. My kids are grown to the age where they can entertain themselves, and don't need constant supervision. So, I decided to check out Ubuntu. I've heard a lot of great things about Ubuntu. I went to their website, and was about to create a LiveCD when I noticed they have a Windows Installer.... huh? Whats this? An executable which installs Linux as a Dual Boot? No hard drive partitions? No figuring out swap size? The culprit is known as Wubi. after a download and a double click, I restarted. My computer started up with a Bootloader menu, displaying a selection for Windows 7 and Ubuntu. I selected Ubuntu and set it up. It was so easy. I almost felt ripped off. I mean, come on, my elitist Linux user attitude could no longer be expressed, since Ubuntu is as simple as Mac OS.

All was going well, for a couple weeks, I was in dual boot bliss. Until, one day, the Update Manager notified me that there was a new version of Grub. I refreshed my memory about what Grub was. Grub is a bootloader. Ok, I will update it.

Then it happened.... a problem with the typical Linux complexity. I rebooted, and in place of the nice bootloader menu, I got the following:
error: no such device: c73d2390-fb23-82c2-63823ae2eac2c
grub rescue>
What is this???? I quickly grab a second laptop and start up google. I search for "grub" and "ubuntu grub rescue". A lot of results are returned. After a while of sifting through the links, I find out that Grub and Wubi don't really play nice. In theory, Wubi has a bootloader which should send control to Grub. That is what was happening before the upgrade. The new version of Grub caused a conflict, such that Wubi didn't run, and Grub didn't know where it was. My initial google search was good for learning about the problem, but didn't yield much in terms of fixing the problem. So, I added "wubi" to the search terms, and I received a lot more results. Many of them told me to go here: "How to restore the Ubuntu/XP/Vista/7 bootloader (Updated for Ubuntu 10.10)". This seemed like the fix I was looking for. Only problem, it wasn't ment as a fix for Wubi installs. Wubi is a different beast all together.

More searching of the ubuntuforums site produced more insight about the problem, and how to ultimately fix it. The solution was to install lilo, another Windows bootloader, and don't ever update Grub. So, I made an Ubuntu LiveCD, and booted from it. That worked nicely and gave me access to a terminal. I then installed lilo, and added it to the mbr (the first sector of the primary hard drive):
sudo apt-get install lilo
sudo lilo -M /dev/sda mbr
After reloading.... It worked :-)
Other forum posts recommended to lock the Grub version in the updater. That is ok to do, but I decided I don't like Wubi anymore. But what can I do? I have a lot of customizations and software currently installed in my current Ubuntu install.

Well, it turns out that if I had only installed Ubuntu as its own partition from the beginning, this wouldn't have happened. But I did not give up hope, googleing "convert wubi install to partition". I found this post: "HOWTO: migrate wubi install to partition". Perfect, just what I needed. Basically, I have to free up some space on my hard drive, aka deallocate it, and create 2 partitions. One for the root /, and the other for the swap space. Windows 7 has an easy way to "shirnk" a partition, which will free up some space on a hard drive. I made the 2 partitions with 60G for root, and 6G for swap (I have 4G of RAM, so 6G sounded good).

Going through the manual steps outlined in the post worked like a charm. I rebooted and could use an updated Grub to load into Ubuntu or Windows. I booted into windows and used "Add/Remove Programs" to delete the Wubi install, since I no longer needed it. All is right in my Ubuntu world. The moral of the story is.... learn to use google.... and beware of Wubi. Wubi isn't bad, but it wasn't obvious that this would be a problem. After all of this.... my elitist Linux user attitude is restored (At least in front of my non-Linux friends).

Wednesday, December 08, 2010

You Know One Programming Language, You Know Them All…(sort of, don’t forget…. Time is Money)

I know that all two of my faithful readers out there (hi mom, hi dad), want to know how my job search has gone. Well… it has gone really well. I found myself a really good gig working for a product based company, that writes software for network tools. But make no mistake, the interviews I went on made me feel like I was back in college. All of a sudden, I was up all night studying the different algorithms to sort trees, explain TCP/IP, and making sure I can discuss the languages I know like the back of my hand, and at least show some understanding of the languages I don’t know.

The job who offered me the job had a pretty grueling, 5 hour interview. I guess I did pretty good considering that many of the questions revolved around Ruby, Perl, and the Rails framework. All of which are concepts I’ve only read about and briefly touched in my early days. During the last 2 weeks at my previous job, I got into some debate about the importance of Computer Science and Software Engineering fundamentals. My old job consisted of quite a few people who believed that if you are a pretty good programmer in one language, then you can easily move to another language. They believe this because, other then some syntax differences, all languages are the same… Right?  Well, sort of.

You see, some languages are compiled, some aren’t. Some languages are object oriented, some aren’t. Some languages are interpreted, and some aren’t. These are all important attributes of a language, and will affect how your code. Interpreted languages are more flexible, but can have some performance issues, so you need to keep that in mind if speed is a need. Object oriented languages are wonderful to design with, but tend to carry a lot of overhead. What if you are a Java programmer, taking a job writing C code? That’s when you realize that garbage collection has made you dependent. But that’s not all. Different software has a different purpose, which is important. Are you going to be writing client/server code? Client only code? Web based applications?  The concept of using HTTP is much different then writing a client side application. 

So, where am I going with this?  I managed to convince this company, which works in an area that I am unfamiliar with, with code that I rarely, if ever use, to hire me. It wasn’t only because I am a pretty good Java programmer, but I showed that I understand computers, networks, software, and the fundamentals of Computer Science and Software Engineering. I showed that I have the initiative to learn what I don’t know, and take the challenges head on.If I would have went into the interview with just my Java history, then I wouldn’t have gotten the job. What my old coworker didn’t understand is that time is money. If you cannot prove that you will be up to speed in a timely manner, then they have to make their decision based on that. Companies don’t want to waste time and money on someone who doesn’t understand fundamentals. Especially when there are others out there who want the job too. You only have a short time during the interview to prove why you are better then then next guy.

During my first couple week at the new job, I’ve been learning all I can and diving right into Ruby on Rails with ext JS. My oop and Java history has helped me pick it up pretty quickly, but I made sure going in that I already started learning. There was one mistake I made the other day, that I spent hours trying to figure out. It wasn’t until I asked the guy in the cube next to me what I was doing wrong, and he picked out a syntax mistake right away. I didn’t realize that back ticks ( ` ) actually meant something Smile But it did show me that syntax is not as trivial as many believe. So my advice to you is to learn about the company that you are interested in. And learn their technologies. At least be able to discuss the concepts, but don’t be afraid to tell them you don’t know something. Just as long as you let them know that you will know it.