Thursday, 28 November 2013

Java WAR deployment options on AWS Elastic Beanstalk

Introduction

Elastic Beanstalk deployment is pretty cool for interpreted languages like Python, Ruby, or PHP where all you need to do is a "git aws.push" to deploy the latest version of your application. The AWS Toolkit for Eclipse is also great for Java deployments but what if you want to deploy a manually built WAR? This post lists a few of the options available. It assumes that you have already created an Elastic Beanstalk application using these instructions. The example uses an application name of war-test and a WAR file named sample.war (kindly provided by Apache Tomcat here).

Option 1 - AWS Console

Pretty simple, select your Beanstalk application and click the "Upload and Deploy" button. Select the WAR file and give it a version name, a few minutes later your new version is up and running.

In the background the file is uploaded to an S3 bucket, a new application version linked to the bucket/object and a deployment triggered.

Option 2 - Command line

This is pretty much the same approach as for the AWS Console upload except that you need to manually perform each of the steps using the AWS CLI tools.

Step 1: Find the S3 bucket to upload the file to, this can be done as follows:

aws elasticbeanstalk describe-application-versions --application-name war-test

Which will print something like:
{
    "ApplicationVersions": [
        {
            "ApplicationName": "war-test",
            "VersionLabel": "git-db96ef73b33ba5ae515907c586d133b26b3489b6-1385637920942",
            "Description": "First commit",
            "DateCreated": "2013-11-28T11:25:21.596Z",
            "DateUpdated": "2013-11-28T11:25:21.596Z",
            "SourceBundle": {
                "S3Bucket": "elasticbeanstalk-us-east-1-XXXXXX",
                "S3Key": "git-db96ef73b33ba5ae515907c586d133b26b3489b6-1385637920942.zip"
            }
        },
        {
            "ApplicationName": "war-test",
            "VersionLabel": "Sample Application",
            "SourceBundle": {
                "S3Bucket": "elasticbeanstalk-us-east-1",
                "S3Key": "GenericSampleApplication"
            },
            "DateUpdated": "2013-11-28T11:25:12.781Z",
            "DateCreated": "2013-11-28T11:25:12.781Z"
        }
    ]
}


You are interested in is the "elasticbeanstalk-us-east-1-XXXXXX" bucket (where XXXXXX represents your bucket identifier), use this as the destination for your WAR file. You can probably also use your own (custom bucket) but I have not checked what permissions are needed to allow Elastic Beanstalk to access the files.

Step 2: Copy your WAR file to the S3 bucket identified in Step 1. Using the CLI tools again:

aws s3 cp ./sample.war s3://elasticbeanstalk-us-east-1-XXXXXX/s3-sample.war

Step 3: Create an application version:

aws elasticbeanstalk create-application-version --application-name war-test --version-label s3-upload --source-bundle S3Bucket=elasticbeanstalk-us-east-1-XXXXXX,S3Key=s3-sample.war

Step 4: Identify the environment to update

aws elasticbeanstalk describe-environments --application-name war-test

Which returns something like:

{
    "Environments": [
        {
            "ApplicationName": "war-test",
            "EnvironmentName": "war-test-env",
            "VersionLabel": "git-dd7d815e8251acae3560158b169d652d66479bc1-1385644793798",
            "Status": "Ready",
            "EnvironmentId": "e-bwjydyebw9",
            "EndpointURL": "54.204.44.36",
            "SolutionStackName": "64bit Amazon Linux 2013.09 running Tomcat 7 Java 7",
            "CNAME": "war-test-env-bffkznzafh.elasticbeanstalk.com",
            "Health": "Green",
            "DateUpdated": "2013-11-28T13:20:51.828Z",
            "DateCreated": "2013-11-28T11:25:29.308Z"
        }
    ]
}


Note the value of the EnvironmentName (war-test-env in this example)

Step 5: Update the environment

aws elasticbeanstalk update-environment --environment-name war-test-env --version-label s3-upload

Which will return something like:


{
    "ApplicationName": "war-test",
    "EnvironmentName": "war-test-env",
    "VersionLabel": "s3-upload",
    "Status": "Updating",
    "EnvironmentId": "e-bwjydyebw9",
    "EndpointURL": "54.204.44.36",
    "SolutionStackName": "64bit Amazon Linux 2013.09 running Tomcat 7 Java 7",
    "CNAME": "war-test-env-bffkznzafh.elasticbeanstalk.com",
    "Health": "Grey",
    "DateUpdated": "2013-11-28T14:13:42.836Z",
    "DateCreated": "2013-11-28T11:25:29.308Z",
    "Resources": {}
}


Rerun step 4 until the Health reflects as "Green" which indicates your updated application has been deployed (but does not neccessarily mean it is working).


Option 3 - git aws.push

This is a bit of hack (storing compiled binary files in git) but is still quite cool:

Step 1: Unzip your WAR file into the directory that you ran "eb init" in

unzip sample.war -d war-test/

Step 2: Add and commit code to git (in the eb init directory again

cd war-test
git add .
git commit -m "Some comment" .

Step 3: Deploy

git aws.push

Conclusion

There are probably other ways of doing this, feel free to add a comment if you find a more elegant solution. Some of the approaches may also work with .NET applications although I have not tested them.

Wednesday, 27 November 2013

Self-contained JAR file creation using Maven Shade Plugin

Trick for the day. Adding the Maven Shade Plugin (as described here) to your pom.xml allows you to generate a self-contained executable JAR file including all libraries and dependencies.

"mvn package" to generate the JAR and then "java -jar mypackage.jar" to run. Useful for creating self-contained artifacts for offline distribution or for locking dependency versions.

Wednesday, 20 November 2013

Python cx_Oracle on Ubuntu 12.04

There are some older articles on getting cx_Oracle working using RPMs and alien but it seems Oracle are now providing non-RPM downloads. Below are the (quick and dirty) steps I followed to get it installed on Ubuntu 12.04 x64.

  1. Download both the "Instant Client Package - Basic Lite" and "Instant Client Package - SDK" ZIP files from the Oracle Instant Client download page (taking note of the version of Oracle you are connecting to)
  2. Unzip both the files, they will create a directory correponding to the Oracle version, instantclient_11_2 for example
  3. Change to the instantclient directory created in the previous step and:
    • Create symbolic links to the version specific files: 
      • libclntsh.so -> libclntsh.so.11.1 
      • libocci.so -> libocci.so.11.1
    • Create a lib directory (mkdir lib)
    • Move lib* to the lib directory (mv lib* lib)
    • Move header files from ./sdk/include to . (mv ./sdk/include/*.h .)
  4. Optionally move the instantclient directory to another location
  5. Sudo to root (sudo -s) and build the module:
    • Install the python-dev package (apt-get install python-dev)
    • Export environment variables: 
      • export ORACLE_HOME="path/to/instantclient"
      • export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$ORACLE_HOME/lib"
    • Install the cx_oracle module using pip (pip install cx_oracle)
    • Hopefully get a "Successfully installed cx-oracle" message
  6. Exit the root shell (Ctrl-D)
  7. Add the ORACLE_HOME and LD_LIBRARY_PATH environment variables to your profile (or just manually export them)
  8. Launch python and test:
>>> import cx_Oracle
>>> dsn = cx_Oracle.makedsn('host', port, 'sid')
>>> connection = cx_Oracle.connect('user','password',dsn)
>>> cursor = connection.cursor()
>>> results = cursor.execute("select id from table where ROWNUM <= 10")
>>> cursor.fetchall()
[(705,), (718,), (719,), (721,), (725,), (726,), (727,), (737,), (748,), (769,)]
>>> cursor.close()
>>> connection.close()

Thursday, 14 November 2013

Fixing compile error for AWS Java sample

If you are getting an error like:

~/aws-java-sample/src/main/java/com/amazonaws/samples/S3Sample.java:[94,31] error: for-each loops are not supported in -source 1.3

When trying to build the aws-java-sample from GitHub you can fix it by adding the maven compile source and target (see link). Something like:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>

Fix submitted on GitHub by mvrueden.