Monday, September 09, 2013

Running Java Application as Daemon on EC2 Instance


To run an application as a daemon (similar to Windows service) these are the primary options:

Java Service Wrapper

http://wrapper.tanukisoftware.com/doc/english/launch-nix.html

Apache Jakarta Commons Daemon

http://commons.apache.org/proper/commons-daemon/

Shell Script

http://www.source-code.biz/snippets/java/7.htm

http://stackoverflow.com/questions/11809191/linux-launch-java-program-on-startup-ec2-instance

AWS Linux EC2 instance: java.lang.UnsupportedClassVersionError Unsupported major.minor version 51.0 on

If you are getting this error trying to run a Java application you compiled on Windows and are trying to run on a Linux AWS EC2 instance, it is probably because the AWS EC2 instance comes with an OpenJDK version of Java that is not compatible:

Exception in thread "main" java.lang.UnsupportedClassVersionError: com/radicalsoftware/Main : Unsupported major.minor version 51.0
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:277)
        at java.net.URLClassLoader.access$000(URLClassLoader.java:73)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:212)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
Could not find the main class: com.radicalsoftware.Main. Program will exit.

To fix this problem you could attempt to remove that version of Java and change JAVA_HOME to point to the version of Java you want, however I am not sure if that will cause anything AWS related to malfunction on the Linux instance.

An alternative is just to specify the path to the version of java you want to run when you run your application

So instead of:

java -jar com.radicalsoftware.jar

Use this:

/usr/java/jdk1.7.0_25/java -jar com.radicalsoftware.jar

You'll need to install the Oracle JDK of course as noted in this post:

http://websitenotebook.blogspot.com/2013/09/install-oracle-java-jdk-on-amazon-linux.html

Java Static Initializers

It's always interesting to explore other people's code and learn new constructs or see different ways of doing things. I was just looking at some code that uses static initializers in Java.  Personally I have never used this and wondered if there were any pros and cons to doing things this way.

The pros are that a static initializer will load something that is required by your class once and only once assuming your class was only loaded once over the life of your program. If a class is loaded and unloaded the static initializer will be called twice as noted in this article (and also if the class is loaded by multiple class loaders):

http://www2.sys-con.com/itsg/virtualcd/java/archives/0305/maso/index.html
 
With a static initializer you can throw an exception as well during instantiation of the class.

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html

An example is in the code I am looking at which loads a resource for a program:

 static
    {
        try
        {
            ResourceBundle props= ResourceBundle.getBundle("/com/x/y");
            Enumeration<?> i = props.getKeys();
            while(i.hasMoreElements())
            {
                String prop = (String)i.nextElement();
                String m = props.getString(prop);
                map.put(prop,m);
            }
        }
        catch(MissingResourceException e)
        {
            //do some logging

        }
    } 

Another important point with static initializers is that the variable initialization code in the class will get executed in the order it is written. Therefore you need to have the static initializer before any code that would wipe it out, or use it.

More on static initializers from the Java Language Specification:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.7

As for the use of static variables in general...here are some interesting articles discouraging the use of static:

http://www.offthehill.org/articles/2011/06/03/java-static-variables-are-wrong-almost-always/

http://sensualjava.blogspot.com/2008/12/case-against-static-initializers.html

Statics make testing a nightmare:

http://www.jusfortechies.com/java/core-java/static-blocks.php

Static variables can break unit tests

http://erlend.oftedal.no/blog/?blogid=53

More problems with static variables:

http://gbracha.blogspot.com/2008/02/cutting-out-static.html

Do not use public non final static variables for security reasons:

https://www.securecoding.cert.org/confluence/display/java/OBJ10-J.+Do+not+use+public+static+nonfinal+variables

Global variables are bad

http://c2.com/cgi/wiki?GlobalVariablesAreBad

More notes on security and static variables (initialization to be specific):

http://www.securingjava.com/chapter-seven/chapter-seven-1.html

Install Oracle Java JDK on Amazon Linux AMI

The version of Java on Amazon AWS Linux AMI is not the version of Oracle's JDK.

If you want to use the Oracle / Sun version and upgrade Java version:

wget --no-cookies --header "Cookie: gpw_e24=xxx;" http://download.oracle.com/otn-pub/java/jdk/7u25-b15/jdk-7u25-linux-x64.rpm

install...

sudo rpm -i jdk-7u1-linux-i586.rpm 


Check Java home and will probably be: /usr/lib/jvm/jre

echo $JAVA_HOME

change to path to JDK you installed:

export JAVA_HOME=/usr/java/jdk1.7.0_25
export PATH=$PATH:/usr/java/jdk1.7.0_25/bin

More here:
 
https://gist.github.com/tankchintan/1335220
 
and here:
 
http://www.cyberciti.biz/faq/linux-unix-set-java_home-path-variable/ 

Sunday, September 08, 2013

Copying Files to an AWS Linux EC2 Instance from Windows

You have a couple options for transferring files from a Windows to a Linux server.

One is to use Putty using the Putty Secure Copy client.
The other is to use WinSCP. 
Both are documented here:


You'll want to first launch your instance, make sure your firewall (security group) rules are set appropriately to allow traffic on the appropriate ports and make sure your instance is assigned an Elastic IP so it is accessible from the Internet.


Java Application Using AWS IAM Roles

Using AWS IAM roles prevents having to store credentials on your EC2 instances. More about the benefits here:

http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html

Setting up your application and instance to use IAM roles is pretty easy.

1. Change your Java application to use these constructs wherever you are instantiating an AWS client for some service:

In the sample code you'll see instantiation of AWS clients for various services like this:

 AmazonSQS sqs = new AmazonSQSAsyncClient(
   new ClasspathPropertiesFileCredentialsProvider());
       

Replace the ClassPathPropertiesFileCredentialsProvider (which gets your AWS credentials from a file) with the InstanceProfileCredentialsProvider (which gets temporary security tokens from the service running on the EC2 instance).

 AmazonSQS sqs = new AmazonSQSAsyncClient(
   new InstanceProfileCredentialsProvider());  
 
2.Create a role which has the appropriate permissions. From services, choose IAM. Then choose Roles on the left and click Create New Role on the top as shown below. Give your role a name and click continue.



Add permissions to the role- choose Amazon EC2.


 Select service you want your EC2 instance to access.



Customize the policies if you need - continue.



And...Create Role



If you want to add additional permissions you can attach more policies.



3. Launch and instance and assign the role - assuming here you know how to launch an instance. That is covered in previous blog entries.



4. Copy your code up to the instance and run your application. If it worked with the security credential provider on your local machine with a file on the class path it should work with the instance security provider on the EC2 instance.