{"id":307,"date":"2016-10-17T19:40:51","date_gmt":"2016-10-18T03:40:51","guid":{"rendered":"http:\/\/www.nathanbak.com\/?p=307"},"modified":"2026-01-02T20:04:41","modified_gmt":"2026-01-03T04:04:41","slug":"running-a-java-program-every-15-minutes","status":"publish","type":"post","link":"https:\/\/nathanbak.com\/?p=307","title":{"rendered":"Running a Java program every 15 minutes"},"content":{"rendered":"<p>I wrote a simple Java program that I wanted to run every 15 minutes.\u00a0 I decided to wrap everything into a Docker image so that I would get the logging, restart capability, and portability goodness that one gets for free when running a Docker container.\u00a0 It&#8217;s not a difficult thing to do, but it took me longer than it should since I made some incorrect assumptions.<\/p>\n<p>Since I wanted the image to be small, I went with an &#8220;Alpine&#8221; version of the <a href=\"https:\/\/hub.docker.com\/_\/openjdk\/\">openjdk image<\/a>.\u00a0 My first incorrect assumption was that I could use cron and crontab like I do on Ubuntu or Red Hat systems&#8211;but Alpine doesn&#8217;t come with cron.\u00a0 However, it&#8217;s actually easier than messing with crontab&#8211;I just had to put my script into the \/etc\/periodic\/15min directory.<\/p>\n<p>Once I had the script in place, I tried to run the container, but eventually discovered the the small Alpine image does not have the daemon enabled when the container starts up.\u00a0 This was solved by running crond in the foreground.\u00a0 Here&#8217;s a Dockerfile showing the important bits:<\/p>\n<pre>ROM openjdk:alpine\r\nMAINTAINER Nathan Bak &lt;dockerhub@yellowslicker.com&gt;\r\n\r\n# Create directory to store jars and copy jars there\r\nRUN mkdir \/jars\r\nCOPY jars\/*.jar \/jars\/\r\n\r\n# Copy bin directory of project to root directory\r\nCOPY bin\/ \/\r\n\r\n# Copy runJavaApp script into correct location and modify permissions\r\nCOPY runJavaApp \/etc\/periodic\/15min\/\r\nRUN chmod 755 \/etc\/periodic\/15min\/runJavaApp\r\n\r\n# When the container starts, run crond in foreground and direct the output to stderr\r\nCMD [\"crond\", \"-f\", \"-d\", \"8\"]<\/pre>\n<p>Here is the runJavaApp script:<\/p>\n<pre>#!\/bin\/sh\r\njava -cp \/:\/jars\/commons-codec-1.9.jar:\/jars\/commons-logging-1.2.jar:\/jars\/fluent-hc-4.5.2.jar:\/jars\/httpclient-4.5.2.jar:\/jars\/httpclient-cache-4.5.2.jar:\/jars\/httpclient-win-4.5.2.jar:\/jars\/httpcore-4.4.4.jar:\/jars\/httpmime-4.5.2.jar:\/jars\/jna-4.1.0.jar:\/jars\/jna-platform-4.1.0.jar com.yellowslicker.sample.Client<\/pre>\n<p>The &#8220;gotchas&#8221; I ran into with the script include:<\/p>\n<ol>\n<li>The script must begin with #!\/bin\/sh (out of habit I tried #!\/bin\/bash, but Alpine doesn&#8217;t come with bash)<\/li>\n<li>Each jar must be listed explicitly (I tried using \/:\/jars\/*.jar for my path, but it didn&#8217;t work)<\/li>\n<\/ol>\n<p>There are a lot of ways to schedule things, but this method was simple (once I figured it out) and I think it is robust.\u00a0 In my case, it also fits well into the Docker microservice environment I&#8217;m running.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I wrote a simple Java program that I wanted to run every 15 minutes.\u00a0 I decided to wrap everything into a Docker image so that I would get the logging, restart capability, and portability goodness that one gets for free when running a Docker container.\u00a0 It&#8217;s not a difficult thing to do, but it took &hellip; <a href=\"https:\/\/nathanbak.com\/?p=307\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Running a Java program every 15 minutes&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[8,16],"class_list":["post-307","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-docker","tag-java"],"_links":{"self":[{"href":"https:\/\/nathanbak.com\/index.php?rest_route=\/wp\/v2\/posts\/307","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nathanbak.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nathanbak.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nathanbak.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nathanbak.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=307"}],"version-history":[{"count":2,"href":"https:\/\/nathanbak.com\/index.php?rest_route=\/wp\/v2\/posts\/307\/revisions"}],"predecessor-version":[{"id":309,"href":"https:\/\/nathanbak.com\/index.php?rest_route=\/wp\/v2\/posts\/307\/revisions\/309"}],"wp:attachment":[{"href":"https:\/\/nathanbak.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=307"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nathanbak.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=307"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nathanbak.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=307"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}