{"id":197,"date":"2026-10-12T18:21:24","date_gmt":"2026-10-12T18:21:24","guid":{"rendered":"http:\/\/www.scmgalaxy.com\/tutorials\/2010\/03\/10\/running-java-ee-applications-on-amazon-ec2\/"},"modified":"2026-10-12T18:21:24","modified_gmt":"2026-10-12T18:21:24","slug":"running-java-ee-applications-on-amazon-ec2","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/running-java-ee-applications-on-amazon-ec2\/","title":{"rendered":"Deploying Java EE on Amazon EC2 with EC2Deploy: Workflow, Script, and Modern Lessons"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>Originally published on 2010-03-10; fully revised and updated.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This article explains a classic pattern for running Java EE applications on <a href=\"http:\/\/www.amazon.com\/gp\/browse.html?node=201590011\" target=\"_blank\" rel=\"noopener\">Amazon EC2<\/a> using a Groovy framework called EC2Deploy. If you maintain legacy deployment automation or want to understand early cloud-era deployment design, this walkthrough shows the end-to-end flow: infrastructure launch, database and app server setup, WAR deployment, load testing, and environment teardown.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why this approach mattered for Java EE teams<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The original post addressed a real pain point: performance and environment testing required hardware that was expensive and slow to obtain. EC2 made on-demand servers possible, and EC2Deploy wrapped that capability into a developer-friendly API for multi-node test environments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The source article also referenced then-current EC2 pricing and server-count limits, plus selectable <a href=\"http:\/\/www.amazon.com\/gp\/browse.html?node=370375011\" target=\"_blank\" rel=\"noopener\">server types<\/a>. Treat those figures as historical context, not current AWS pricing or quotas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">EC2Deploy workflow and architecture<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">At a high level, EC2Deploy reads AWS properties, builds a cluster specification, starts EC2 instances, configures MySQL\/Tomcat\/Apache, deploys the application, runs JMeter tests, and then stops the instances.<\/p>\n\n\n\n<pre class=\"mermaid\">\nflowchart TD\n    A[Groovy test script] --> B[Load aws.properties]\n    B --> C[Build ClusterSpec]\n    C --> D[Call cluster.start]\n    D --> E[Launch EC2 instances]\n    E --> F[Init MySQL master and slave]\n    F --> G[Upload DML and WAR to S3 cache]\n    G --> H[Deploy app on Tomcat]\n    H --> I[Configure Apache load balancer]\n    I --> J[Run cluster.loadTest with JMeter]\n    J --> K[Collect CPU latency throughput report]\n    K --> L[Call cluster.stop]\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Core API objects and methods<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The original code uses a small set of objects and fluent methods to describe both infrastructure and application behavior.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Object or method<\/th><th>Role in the workflow<\/th><\/tr><\/thead><tbody><tr><td>AWSProperties(&#8220;\/path\/to\/aws.properties&#8221;)<\/td><td>Loads AWS credentials and AMI-related properties.<\/td><\/tr><tr><td>new EC2(awsProperties)<\/td><td>Creates the EC2 client wrapper used by EC2Deploy.<\/td><\/tr><tr><td>ClusterSpec.schema(&#8230;)<\/td><td>Defines DB schema name, user\/password map, and DML scripts.<\/td><\/tr><tr><td>ClusterSpec.slaves(1)<\/td><td>Specifies the number of MySQL slave nodes.<\/td><\/tr><tr><td>ClusterSpec.tomcats(1)<\/td><td>Specifies the number of Tomcat nodes.<\/td><\/tr><tr><td>ClusterSpec.webApp(explodedWar, &#8220;ptrack&#8221;)<\/td><td>Declares exploded WAR path and context root.<\/td><\/tr><tr><td>ClusterSpec.catalinaOptsBuilder(&#8230;)<\/td><td>Builds Tomcat JVM and app properties, including DB host wiring.<\/td><\/tr><tr><td>cluster.start()<\/td><td>Provisions instances and performs configuration\/deployment steps.<\/td><\/tr><tr><td>cluster.loadTest(&#8220;&#8230;SimpleTest.jmx&#8221;, [1,2])<\/td><td>Runs JMeter tests at multiple thread counts.<\/td><\/tr><tr><td>cluster.stop()<\/td><td>Stops the EC2 instances when testing is complete.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Annotated Groovy example<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The following script is a cleaned-up version of the original example, preserving the same structure and intent.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript language-groovy\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ClusterTest<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">GroovyTestCase<\/span> <\/span>{\n  <span class=\"hljs-keyword\">void<\/span> testSomething() {\n    AWSProperties awsProperties = <span class=\"hljs-keyword\">new<\/span> AWSProperties(<span class=\"hljs-string\">\"\/path\/to\/aws.properties\"<\/span>)\n    def ec2 = <span class=\"hljs-keyword\">new<\/span> EC2(awsProperties)\n\n    def explodedWar = <span class=\"hljs-string\">\"\/path\/to\/projecttrack\/webapp\/target\/ptrack\"<\/span>\n\n    ClusterSpec clusterSpec = <span class=\"hljs-keyword\">new<\/span> ClusterSpec()\n      .schema(<span class=\"hljs-string\">\"ptrack\"<\/span>, &#91;<span class=\"hljs-string\">\"ptrack\"<\/span>: <span class=\"hljs-string\">\"ptrack\"<\/span>], &#91;\n        <span class=\"hljs-string\">\"src\/test\/resources\/testdml1.sql\"<\/span>,\n        <span class=\"hljs-string\">\"src\/test\/resources\/testdml2.sql\"<\/span>\n      ])\n      .slaves(<span class=\"hljs-number\">1<\/span>)\n      .tomcats(<span class=\"hljs-number\">1<\/span>)\n      .webApp(explodedWar, <span class=\"hljs-string\">\"ptrack\"<\/span>)\n      .catalinaOptsBuilder({ builder, databasePrivateDnsName -&gt;\n        builder.arg(<span class=\"hljs-string\">\"-Xmx500m\"<\/span>)\n        builder.prop(<span class=\"hljs-string\">\"com.sun.management.jmxremote\"<\/span>)\n        builder.prop(<span class=\"hljs-string\">\"com.sun.management.jmxremote.port\"<\/span>, <span class=\"hljs-number\">8091<\/span>)\n        builder.prop(<span class=\"hljs-string\">\"com.sun.management.jmxremote.authenticate\"<\/span>, <span class=\"hljs-literal\">false<\/span>)\n        builder.prop(<span class=\"hljs-string\">\"com.sun.management.jmxremote.ssl\"<\/span>, <span class=\"hljs-literal\">false<\/span>)\n        builder.prop(<span class=\"hljs-string\">\"ptrack.application.environment\"<\/span>, <span class=\"hljs-string\">\"ec2\"<\/span>)\n        builder.prop(<span class=\"hljs-string\">\"log4j.configuration\"<\/span>, <span class=\"hljs-string\">\"log4j-minimal.properties\"<\/span>)\n        builder.prop(<span class=\"hljs-string\">\"jdbc.db.server\"<\/span>, databasePrivateDnsName)\n      })\n\n    SimpleCluster cluster = <span class=\"hljs-keyword\">new<\/span> SimpleCluster(ec2, clusterSpec)\n    cluster.start()\n    cluster.loadTest(<span class=\"hljs-string\">\"\/path\/to\/functionalTests\/jmeter\/SimpleTest.jmx\"<\/span>, &#91;<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">2<\/span>])\n    cluster.stop()\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">What happens during cluster.start()<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>1) Launch EC2 instances from the configured appliance AMI.<\/li><li>2) Initialize the MySQL master database.<\/li><li>3) Create the MySQL slave.<\/li><li>4) Create schema and users.<\/li><li>5) Run DML scripts.<\/li><li>6) Cache deployment artifacts in Amazon <a href=\"http:\/\/www.amazon.com\/gp\/browse.html?node=16427261\" target=\"_blank\" rel=\"noopener\">S3<\/a>.<\/li><li>7) Deploy web applications on each Tomcat node.<\/li><li>8) Configure Apache to balance traffic across Tomcat nodes.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">In the original description, DML files were cached in a bucket named <strong>tmp&#8211;dml<\/strong>, and web apps were cached in <strong>-tmp-war<\/strong> to avoid repeatedly uploading unchanged files.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Running JMeter load tests with thread ramps<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">EC2Deploy can execute a JMeter plan multiple times using different thread counts. In the source example, it runs once with one thread and then with two threads.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript language-groovy\">cluster.loadTest(<span class=\"hljs-string\">\"\/path\/to\/functionalTests\/jmeter\/SimpleTest.jmx\"<\/span>, &#91;<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">2<\/span>])<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Per the original article, each run produced metrics including CPU utilization, average response time, and throughput.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">EC2Deploy-era pattern vs common modern alternatives<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Need in this workflow<\/th><th>EC2Deploy-era implementation<\/th><th>Common modern option<\/th><\/tr><\/thead><tbody><tr><td>Provision test infrastructure<\/td><td>Groovy wrapper around EC2 APIs<\/td><td>Terraform or AWS CloudFormation<\/td><\/tr><tr><td>Configure node software stack<\/td><td>Custom appliance AMI and startup logic<\/td><td>Immutable images plus config automation<\/td><\/tr><tr><td>Distribute deployment artifacts<\/td><td>S3 cache buckets for DML and exploded WAR files<\/td><td>Artifact repositories and object storage pipelines<\/td><\/tr><tr><td>Run load tests programmatically<\/td><td>JMeter invoked by cluster.loadTest()<\/td><td>JMeter in CI\/CD jobs or dedicated test runners<\/td><\/tr><tr><td>Tear down temporary environments<\/td><td>cluster.stop()<\/td><td>Automated destroy stages in CI\/CD<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Common pitfalls and best practices<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Do not reuse historical EC2 price or quota assumptions for planning.<\/li><li>Keep credentials out of source-controlled property files; use secret management.<\/li><li>Treat performance environments as ephemeral and automate full teardown to control cost.<\/li><li>Validate old S3 bucket naming and permissions against current security policies.<\/li><li>Prefer reproducible infrastructure definitions over long-lived manually tuned instances.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Historical note preserved from the source: EC2Deploy and an associated Maven plugin were announced as available in this post: <a href=\"http:\/\/www.chris-richardson.blog-city.com\/ec2deploy_and_the_cloud_tools_maven_plugin_are_now_available.htm\">EC2Deploy and the Cloud Tools Maven plugin are now available<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This revised guide breaks down the original EC2Deploy approach for provisioning EC2 instances, deploying Java EE apps, and running JMeter tests. It also maps the same deployment pattern to modern DevOps practices.<\/p>\n","protected":false},"author":1,"featured_media":4109,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[5633],"tags":[3354,310,162,3357,3359,365,1122,3113,3361,3360,1806,173,3353,3358,3356,562,938,3355,213],"class_list":["post-197","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aws","tag-amazon-ec2","tag-application","tag-aws","tag-deploy","tag-deploy-java-ee-app-on-amazon-ec2","tag-ec2deploy","tag-guide","tag-how-to","tag-how-to-deploe-java-ee-app-on-ec2","tag-how-to-run-java-ee-app-on-ec2","tag-instruction","tag-java","tag-java-ee","tag-java-ee-app-on-ec2","tag-java-ee-application-on-amazon-ec2","tag-push","tag-reference","tag-run-java-ee-applications-on-amazon-ec2","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/197","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=197"}],"version-history":[{"count":3,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/197\/revisions"}],"predecessor-version":[{"id":79027,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/197\/revisions\/79027"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media\/4109"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=197"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=197"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=197"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}