{"id":69790,"date":"2017-10-23T09:47:03","date_gmt":"2017-10-23T06:47:03","guid":{"rendered":"http:\/\/www.javacodegeeks.com\/?p=69790"},"modified":"2023-12-11T10:26:48","modified_gmt":"2023-12-11T08:26:48","slug":"docker-java-developers-docker-http-rest","status":"publish","type":"post","link":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html","title":{"rendered":"Docker for Java Developers: Docker over HTTP\/REST"},"content":{"rendered":"<p><em>This article is part of our Academy Course titled <a href=\"https:\/\/www.javacodegeeks.com\/2018\/02\/docker-tutorial-java-developers.html\">Docker Tutorial for Java Developers<\/a>.<\/p>\n<p>In this course, we provide a series of tutorials so that you can develop your own Docker based applications. We cover a wide range of topics, from Docker over command line, to development, testing, deployment and continuous integration. With our straightforward tutorials, you will be able to get your own projects up and running in minimum time. Check it out <a href=\"https:\/\/www.javacodegeeks.com\/2018\/02\/docker-tutorial-java-developers.html\">here<\/a>!<\/em><\/p>\n<div class=\"toc\">\n<h3>Table Of Contents<\/h3>\n<dl>\n<dt><a href=\"#introduction\">1. Introduction<\/a><\/dt>\n<dt><a href=\"#versioning\">2. Versioning<\/a><\/dt>\n<dt><a href=\"#started\">3. Getting Started<\/a><\/dt>\n<dt><a href=\"#images\">4. Images<\/a><\/dt>\n<dt><a href=\"#containers\">5. Containers<\/a><\/dt>\n<dt><a href=\"#ports\">6. Ports<\/a><\/dt>\n<dt><a href=\"#volumes\">7. Volumes<\/a><\/dt>\n<dt><a href=\"#networks\">8. Networks<\/a><\/dt>\n<dt><a href=\"#limits\">9. Resource Limits<\/a><\/dt>\n<dt><a href=\"#primer\">10. The Primer<\/a><\/dt>\n<dt><a href=\"#conclusions\">11. Conclusions<\/a><\/dt>\n<dt><a href=\"#next\">12. What\u2019s next<\/a><\/dt>\n<\/dl>\n<\/div>\n<h2><a name=\"introduction\"><\/a>1. Introduction<\/h2>\n<p>From the previous parts of the tutorial we already know that <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> not only has an awesome command line tooling, but exposes a feature-rich <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Docker Engine API<\/a> as well. As of now, the officially supported clients are provided for Go and Python languages. Certainly, not very encouraging news for Java developer, but there is a light at the end of the tunnel.<\/p>\n<p>Quite a long time ago <a href=\"https:\/\/spotify.github.io\/\">Spotify<\/a> started its own journey to develop a Java client for the <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Docker Engine API<\/a> (more widely known as <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a>). The project is being actively maintained ever since to follow all the recent developments in <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> \/ <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Engine API<\/a> space and is the de-facto choice for JVM based applications nowadays. It does the job really well but has one caveat you should be aware of: because this is a community effort, not the official Java client from <a href=\"https:\/\/www.docker.com\/\">Docker<\/a>, it takes some time to catch up with all the changes introduced by frequent <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> releases.<\/p>\n<p>In this section of the tutorial we will go over a few key <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Docker Engine APIs<\/a> and demonstrate how those could be consumed from your Java applications using <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a>.<\/p>\n<h2><a name=\"versioning\"><\/a>2. Versioning<\/h2>\n<p>Before getting started, we have to understand how <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Docker Engine API<\/a> is <a href=\"https:\/\/docs.docker.com\/engine\/api\/v1.30\/#section\/Versioning\">versioned<\/a> and how to match the <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> release and the version of the API it supports. The <a href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/version\/\">version<\/a> command of the <a href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/cli\/\">docker<\/a> command line tool is what we need.<\/p>\n<pre class=\"brush:bash\">$ docker version\n\nClient:\n Version:      17.06.1-ce\n API version:  1.30\n Go version:   go1.8.3\n Git commit:   874a737\n Built:        Thu Aug 17 22:48:20 2017\n OS\/Arch:      linux\/amd64\n\nServer:\n Version:      17.06.1-ce\n API version:  1.30 (minimum version 1.12)\n Go version:   go1.8.3\n Git commit:   874a737\n Built:        Thu Aug 17 22:54:55 2017\n OS\/Arch:      linux\/amd64\n Experimental: false\n<\/pre>\n<p>The output is pretty straightforward. We could easily spot that our version of <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> is <code>17.06.1-ce<\/code>\u00a0 and its supported <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">API<\/a> version is <code>1.30<\/code>.\u00a0 From that, let us bookmark the <a href=\"https:\/\/docs.docker.com\/engine\/api\/v1.30\/\">reference to Docker Engine API v1.30 documentation<\/a> and roll up the sleeves.<\/p>\n<h2><a name=\"started\"><\/a>3. Getting Started<\/h2>\n<p>To begin with, we would need to introduce the respective <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> dependency to our projects. In case of <a href=\"https:\/\/maven.apache.org\/\">Apache Maven<\/a>, it is fairly easy:<\/p>\n<pre class=\"brush:xml\">&lt;dependency&gt;\n\t&lt;groupId&gt;com.spotify&lt;\/groupId&gt;\n\t&lt;artifactId&gt;docker-client&lt;\/artifactId&gt;\n\t&lt;version&gt;8.9.1&lt;\/version&gt;\n&lt;\/dependency&gt;<\/pre>\n<p>As everything else in <a href=\"https:\/\/www.docker.com\/\">Docker<\/a>&#8216;s ecosystem, the <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Engine API<\/a> evolves pretty fast so it is highly recommended to rely on the most recent <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> release. As of now, <code>8.9.1<\/code> is the latest one we could use.<\/p>\n<p>Awesome, dependency is there, so we could go ahead and create an instance of <code>DockerClient<\/code> class. There are a couple of ways to do that and, essentially, it really depends on which operating system (or systems) you are using (either in development, continuous integration or production environments).<\/p>\n<p>Probably, the quickest, safest and most portable approach would be to use <code>DefaultDockerClient.fromEnv<\/code> \u00a0method which will gather the required details from the environment variables and will work across a range of <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> installations, including older Mac and Windows systems were <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> is not natively supported (things like <a href=\"https:\/\/docs.docker.com\/toolbox\/toolbox_install_windows\/\">Docker Toolbox<\/a> or <a href=\"https:\/\/github.com\/boot2docker\/boot2docker\">boot2docker<\/a>).<\/p>\n<pre class=\"brush:java\">final DockerClient client = DefaultDockerClient\n\t.fromEnv()\n\t.build();\n<\/pre>\n<p>Here we just assume the default <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> configuration is in place but you have a total <a href=\"https:\/\/github.com\/spotify\/docker-client\/blob\/master\/docs\/user_manual.md#creating-a-docker-client\">control over customizing<\/a> it, including connection URIs, registry authentication, connection pooling, etc. \u00a0It is important to remember that when you are done with the instance of the client, please close it, for example:<\/p>\n<pre class=\"brush:java\">client.close();\n<\/pre>\n<p>Great, we have our <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> client instance. It is a single entry point to call numerous <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Docker Engine APIs<\/a>, let us take a look what we could accomplish with it.<\/p>\n<h2><a name=\"images\"><\/a>4. Images<\/h2>\n<p>The first <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Engine API<\/a> we are going to look at is <a href=\"https:\/\/docs.docker.com\/engine\/api\/v1.30\/#tag\/Image\">Images API<\/a>. The <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> supports most of its features through a family of methods. To illustrate that, let us borrow the <a href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/\">Dockerfile<\/a> from the previous section of the tutorial and store its content in the <code>src\/main\/resources<\/code> folder, inside the file with name <code>Dockerfile<\/code>:<\/p>\n<pre class=\"brush:bash\">FROM openjdk:8u131-jdk-alpine\nCMD [\"java\", \"-version\"]\n<\/pre>\n<p>And now, let us build the image from it using <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a>:<\/p>\n<pre class=\"brush:java\">final URL dockerfile = getClass().getResource(\"\/\");\n\t\t\nfinal String imageId = client.build(Paths.get(dockerfile.toURI()), \n\tBuildParam.name(\"base:openjdk-131-jdk\"));\n<\/pre>\n<p>Easy, but how could we check if the image is there? Our old buddy <a href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/cli\/\">docker<\/a> command line tool comes into mind first, but why not to use <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> for that?<div style=\"display:inline-block; margin: 15px 0;\"> <div id=\"adngin-JavaCodeGeeks_incontent_video-0\" style=\"display:inline-block;\"><\/div> <\/div><\/p>\n<pre class=\"brush:java\">final ImageInfo imageInfo = client.inspectImage(imageId);\n<\/pre>\n<p>Nice, what about fetching all available images?<\/p>\n<pre class=\"brush:java\">final List allImages = client.listImages();\n<\/pre>\n<p>What if we need to add additional tags to the image?<\/p>\n<pre class=\"brush:java\">client.tag(imageId, \"openjdk\");\n<\/pre>\n<p>It is also possible to get image history, for example:<\/p>\n<pre class=\"brush:java\">final List history = client.history(imageId);\n<\/pre>\n<p>In case we do not need this image anymore, it could be easily removed:<\/p>\n<pre class=\"brush:java\">final List removedImages = client.removeImage(imageId, true, false);\n<\/pre>\n<p>We can also search images in the <a href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a> registry. For example, let us look up for all available JDK images:<\/p>\n<pre class=\"brush:java\">final List jdkImages = client.searchImages(\"jdk\");\n<\/pre>\n<p>To interface with registries, there are pull and push methods. The first one fetches the image from a registry while the latter uploads it to a registry.<\/p>\n<pre class=\"brush:java\">client.pull(\"base:openjdk-131-jdk\");\nclient.push(\"base:openjdk-131-jdk\");\n<\/pre>\n<p>In order to get more details related to images manipulation, please check out the <a href=\"https:\/\/github.com\/spotify\/docker-client\/blob\/master\/docs\/user_manual.md#images\">the official Spotify Docker Client documentation<\/a>.<\/p>\n<h2><a name=\"containers\"><\/a>5. Containers<\/h2>\n<p>The next <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Engine API<\/a> we are going to talk about is <a href=\"https:\/\/docs.docker.com\/engine\/api\/v1.30\/#tag\/Container\">Containers API<\/a>, arguably the richest one from the functionality perspective.<\/p>\n<p>To make an use case a bit more realistic, let us spawn <a href=\"https:\/\/hub.docker.com\/_\/mysql\/\">MySQL<\/a> container instance from <code>mysql:8.0.2<\/code> image, passing some environment variables and exposing at least one port.<\/p>\n<pre class=\"brush:java\">final ContainerCreation container = client.createContainer(ContainerConfig\n\t.builder()\n\t.image(\"mysql:8.0.2\")\n\t.env(\n\t\t\"MYSQL_ROOT_PASSWORD=p$ssw0rd\", \n\t\t\"MYSQL_DATABASE=my_app_db\"\n\t)\n\t.exposedPorts(\"3306\")\n\t.hostConfig(\n\t\tHostConfig\n\t\t\t.builder()\n\t\t\t.portBindings(\n\t\t\t\tImmutableMap.of(\n\t\t\t\t\t\"3306\", \n\t\t\t\t\tImmutableList.of(\n\t\t\t\t\t\tPortBinding.of(\"0.0.0.0\", 3306)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.build()\n\t)\n\t.build()\n);\n<\/pre>\n<p>In order for container creation to succeed, we would need to have <code>mysql:8.0.2<\/code> available locally. If you are not sure you have one, it is always better to pull the image before using it.<\/p>\n<pre class=\"brush:java\">client.pull(\"mysql:8.0.2\");\n<\/pre>\n<p>At this point the container is created but not started yet, so let us run it:<\/p>\n<pre class=\"brush:java\">client.startContainer(container.id());\n<\/pre>\n<p>Having the container up and running, there are quite a few things which could be done with it. We could get all the details about the container by inspecting it:<\/p>\n<pre class=\"brush:java\">final ContainerInfo info = client.inspectContainer(container.id());\n<\/pre>\n<p>Pretty straightforward, but what if we need to capture the logs or the standard output from the container? This could be accomplished by attaching to the running container, for example:<\/p>\n<pre class=\"brush:java\">client\n\t.attachContainer(container.id(), AttachParameter.values())\n\t.attach(System.out, System.err, false);\n<\/pre>\n<p>There is one caveat though, the last attach invocation will block the calling thread, waiting for the output from the container. This is probably not what most of us expect so we should better offload the invocation to dedicated thread.<\/p>\n<pre class=\"brush:java\">executor.submit(\n        () -&gt; {\n\t\tclient\n\t\t\t.attachContainer(container.id(), AttachParameter.values())\n\t\t\t.attach(System.out, System.err, false);\n\t\treturn null;\n\t});\t\n\n<\/pre>\n<p>You have the control over the state of the processes within the container by pausing \/ unpausing them, for example:<\/p>\n<pre class=\"brush:java\">client.pauseContainer(container.id());\nclient.unpauseContainer(container.id());\n<\/pre>\n<p>There are also the APIs exposed to fetch the runtime statistics about the container or to get the list of running container processes.<\/p>\n<pre class=\"brush:java\">final ContainerStats stats = client.stats(container.id());\nfinal TopResults top = client.topContainer(container.id());\n<\/pre>\n<p>Finally, there is a dedicated API to fetch and tail the logs of the container, quite similar to the <code>attachContainer<\/code> method we have discussed above.<\/p>\n<pre class=\"brush:java\">client\n    .logs(container.id(), LogsParam.stdout(), LogsParam.stderr(), LogsParam.tail(10))\n    .attach(System.out, System.err, false);\n<\/pre>\n<p>Once the container is not needed anymore, it could be stopped and terminated right after.<\/p>\n<pre class=\"brush:java\">client.stopContainer(container.id(), 5 \/* wait 5 seconds before killing *\/);\nclient.removeContainer(container.id());\n<\/pre>\n<p>In order to get more details related to containers manipulation, please check out <a href=\"https:\/\/github.com\/spotify\/docker-client\/blob\/master\/docs\/user_manual.md#containers\">the official Spotify Docker Client documentation<\/a>.<\/p>\n<p>So we know how to deal with images and containers using purely <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Docker Engine API<\/a>s with a help of <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a>. Let us talk about equally important topics like managing ports, networks, volumes and resource limits.<\/p>\n<h2><a name=\"ports\"><\/a>6. Ports<\/h2>\n<p>Every container could expose ports to listen to at runtime, either through image <a href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/\">Dockerfile<\/a> instructions or through the arguments of the <code>createContainer<\/code> method invocation. The <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> does not provide the specialized method to fetch the ports and their mappings but this information could be easily extracted from <code>ContainerInfo<\/code>, for example:<\/p>\n<pre class=\"brush:java\">final ContainerInfo info = client.inspectContainer(container.id());\nfinal ImmutableMap&lt;String, List&gt; mappings = info.hostConfig().portBindings();\n<\/pre>\n<p>In fact, a more reliable way would be to use <code>info.networkSettings().ports()<\/code> as the container may not necessarily use port mappings, but only expose some ports.<\/p>\n<pre class=\"brush:java\">final ContainerInfo info = client.inspectContainer(container.id());\nfinal ImmutableMap&lt;String, List&gt; mappings = info.networkSettings().ports();\n<\/pre>\n<p>[ulp id=&#8217;MD25RnPuC2vrVItl&#8217;]<br \/>\n&nbsp;<\/p>\n<h2><a name=\"volumes\"><\/a>7. Volumes<\/h2>\n<p>In the <a href=\"https:\/\/www.javacodegeeks.com\/2017\/09\/docker-java-developers-docker-command-line.html\">previous section of the tutorial<\/a> we have learned about <a href=\"https:\/\/docs.docker.com\/engine\/admin\/volumes\/volumes\/\">volumes<\/a> as the preferred mechanism for persisting data used by containers. The <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> comprehensively wraps <a href=\"https:\/\/docs.docker.com\/engine\/api\/v1.30\/#tag\/Volume\">Volumes API<\/a> through the family of methods. Let us start by creating a new volume:<\/p>\n<pre class=\"brush:java\">final Volume volume = client.createVolume();\n<\/pre>\n<p>It is possible to get the metadata about the existing volume:<\/p>\n<pre class=\"brush:java\">final Volume info = client.inspectVolume(volume.name());\n<\/pre>\n<p>Also, you could fetch all available volumes (and filter them if necessary), for example:<\/p>\n<pre class=\"brush:java\">final VolumeList volumes = client.listVolumes();\n<\/pre>\n<p>Once the volume is not needed anymore, it could be removed.<\/p>\n<pre class=\"brush:java\">client.removeVolume(volume.name());\n<\/pre>\n<p>In order to get more details related to volumes manipulation, please check out <a href=\"https:\/\/github.com\/spotify\/docker-client\/blob\/master\/docs\/user_manual.md#volumes\">the official Spotify Docker Client documentation<\/a>.<\/p>\n<h2><a name=\"networks\"><\/a>8. Networks<\/h2>\n<p>The importance of the <a href=\"https:\/\/docs.docker.com\/engine\/userguide\/networking\/work-with-networks\/#connect-containers\">user-defined networks<\/a> in <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> is hard to overestimate as they play a critical role in how containers communicate which each other. Not surprisingly, the <a href=\"https:\/\/docs.docker.com\/engine\/api\/v1.30\/#tag\/Network\">Networks API<\/a> is supported mostly in full power by <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a>.<\/p>\n<p>The creation of a new network is just one method away:<\/p>\n<pre class=\"brush:java\">final NetworkCreation network = client.createNetwork(\n\tNetworkConfig\t\t\t\n\t\t.builder()\n\t\t.name(\"test-network\")\n\t\t.driver(\"bridge\")\n\t\t.build()\n\t);\n<\/pre>\n<p>If you know the network identifier, you could fetch its metadata:<\/p>\n<pre class=\"brush:java\">final Network info = client.inspectNetwork(network.id());\n<\/pre>\n<p>If you do not know the network identifier or just interested in querying (and filtering) all networks, you could do that as well:<\/p>\n<pre class=\"brush:java\">final List networks = client.listNetworks();\n<\/pre>\n<p>Once the network is not needed anymore, it could be easily removed.<\/p>\n<pre class=\"brush:java\">client.removeNetwork(network.id());\n<\/pre>\n<p><a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> provides the means to connect to the network or disconnect from the network for any running (or just created) container, for example:<\/p>\n<pre class=\"brush:java\">final ContainerCreation container = client.createContainer(ContainerConfig\n\t\t.builder()\n\t\t.image(\"mysql:8.0.2\")\n\t\t.env(\n\t\t\t\"MYSQL_ROOT_PASSWORD=p$ssw0rd\", \n\t\t\t\"MYSQL_DATABASE=my_app_db\"\n\t\t)\n\t\t.exposedPorts(\"3306\")\n\t\t.build()\n\t);\n\nclient.startContainer(container.id());\nclient.connectToNetwork(container.id(), network.id());\n<\/pre>\n<p>Or:<\/p>\n<pre class=\"brush:java\">client.disconnectFromNetwork(container.id(), network.id());\n<\/pre>\n<p>As of now, <a href=\"https:\/\/github.com\/spotify\/docker-client\/blob\/master\/docs\/user_manual.md\">the official Spotify Docker Client documentation<\/a> does not include a section dedicated to networks manipulation.<\/p>\n<h2><a name=\"limits\"><\/a>9. Resource Limits<\/h2>\n<p>The <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> also supports the ability to dynamically adjust the container configuration (primary, resource limits like memory, CPU, or block I\/O), for example:<\/p>\n<pre class=\"brush:java\">final ContainerUpdate update = client.updateContainer(container.id(), \n\tHostConfig\n\t\t.builder()\n\t\t.memory(268435456L \/* limit to 256Mb *\/)\n\t\t.build()\n\t);\n<\/pre>\n<h2><a name=\"primer\"><\/a>10. The Primer<\/h2>\n<p>To finish up this section, it would be great to have a realistic example where all the APIs we have looked at so far work seamlessly together.\u00a0 As we have been playing mostly with <a href=\"https:\/\/hub.docker.com\/_\/mysql\/\">MySQL<\/a>, quite popular choice as a relational data store for Java (and not only) applications, the end-to-end demo would be based on it. So what are our goals?<\/p>\n<ol>\n<li>Create a container instance using <code>mysql:8.0.2<\/code> image<\/li>\n<li>Expose TCP port <code>3306<\/code> and bind it to a random host port (so we could run as many containers as we want, without having a port conflicts)<\/li>\n<li>Make sure the container is started and <a href=\"https:\/\/hub.docker.com\/_\/mysql\/\">MySQL<\/a> server process is ready to serve our queries<\/li>\n<li>Connect to the <a href=\"https:\/\/hub.docker.com\/_\/mysql\/\">MySQL<\/a> instance using <a href=\"https:\/\/dev.mysql.com\/doc\/connector-j\/8.0\/en\/\">JDBC driver for MySQL<\/a> and list all catalogs (databases)<\/li>\n<\/ol>\n<p>Sounds like a lot of work, but \u2026 likely we learned about <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a>, so let us take one step at a time to make the miracle happen.<\/p>\n<pre class=\"brush:java\">final DockerClient client = DefaultDockerClient\n\t.fromEnv()\n\t.build();\n\n\/\/ Pull the image first\nclient.pull(\"mysql:8.0.2\");\n\t\t\n\/\/ Create container\nfinal ContainerCreation container = client.createContainer(ContainerConfig\n\t.builder()\n\t.image(\"mysql:8.0.2\")\n\t.env(\n\t\t\"MYSQL_ROOT_PASSWORD=p$ssw0rd\", \n\t\t\"MYSQL_DATABASE=my_app_db\"\n\t)\n\t.exposedPorts(\"3306\")\n\t.healthcheck(\n\t\tHealthcheck\n\t\t\t.create(\n\t\t\t\t\/\/ MySQL image doesn't have `nc` available\n\t\t\t\tArrays.asList(\n\t\t\t\t\t\"CMD-SHELL\", \"ss -ltn src :3306 | grep 3306\"\n                  \t), \n\t\t\t \t5000000000L, \/* 5 seconds, in nanoseconds *\/ \n\t\t\t \t3000000000L, \/* 3 seconds, in nanoseconds *\/\n\t\t\t\t5\n\t\t\t)\n\t)\n\t.hostConfig(\n\t\tHostConfig \n\t\t\t.builder()\n\t\t\t.portBindings(\n\t\t\t\tImmutableMap.of(\n\t\t\t\t\t\"3306\", \n\t\t\t\t\tImmutableList.of(\n\t\t\t\t\t\tPortBinding.of(\"0.0.0.0\", 0 \/* use random port *\/)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.build()\n\t)\n\t.build()\n);\n<\/pre>\n<p>This snippet should be looking familiar except probably the heath check part. Why do we need it? Well, it would be great if <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> tells us not only when container has started but when the application inside the container is fully up and running. This is exactly what the <a href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/#healthcheck\">health checks<\/a> are about. With respect to <a href=\"https:\/\/hub.docker.com\/_\/mysql\/\">MySQL<\/a>, the by the book recipe is to verify if there is a process listening on port <code>3306<\/code>, and this is precisely what we are doing here with <code>ss -ltn src :3306 | grep 3306<\/code> shell command.<\/p>\n<pre class=\"brush:java\">\/\/ Start the container\nclient.startContainer(container.id());\n\t\t\t\n\/\/ Inspect the container's health\nContainerInfo info = client.inspectContainer(container.id());\t\t\nwhile (info.state().health().status().equalsIgnoreCase(\"starting\")) {\n\t\/\/ Await for container's health check to pass or fail\n\tThread.sleep(1000);\n\t\t\t\n\t\/\/ Ask for container status\n\tinfo = client.inspectContainer(container.id());\n\n\t\/\/ Along with health, better to check the container status as well\n\tif (info.state().status().equalsIgnoreCase(\"exited\")) {\n\t\tLOG.info(\"The container {} has exited unexpectedly ...\", container.id());\n\t\tbreak;\n\t}\n}\n<\/pre>\n<p>Nothing complicated in this snippet above as well. We are periodically polling the container and checking its health status, the values we may get here are <code>starting<\/code>, <code>healthy<\/code>, and <code>unhealthy<\/code>. Please notice that we also consult the container status as the health check may not reflect the actual state of the container in some circumstances.<\/p>\n<pre class=\"brush:java\">\/\/ Check if container is healthy\nif (info.state().health().status().equalsIgnoreCase(\"healthy\")) {\n    \/\/ ...\n}\n<\/pre>\n<p>Once the <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> reports to us that the container is healthy, we are ready to open a <a href=\"http:\/\/www.oracle.com\/technetwork\/java\/overview-141217.html\">JDBC<\/a> connection to <a href=\"https:\/\/hub.docker.com\/_\/mysql\/\">MySQL<\/a> instance. But before that, we have to figure out which port to use.<\/p>\n<pre class=\"brush:java\">final PortBinding binding = info\n\t.networkSettings()\n\t.ports()\n\t.get(\"3306\/tcp\")\n\t.get(0);\n\t\t\t\nfinal int port = Integer.valueOf(binding.hostPort());\n<\/pre>\n<p>We have it, let us pass the port directly to <a href=\"http:\/\/www.oracle.com\/technetwork\/java\/overview-141217.html\">JDBC<\/a> connection string and we are almost there! We just need to put in the last piece of the puzzle, the name of the host to connect. Or should it be <code>localhost<\/code>? The answer is: it really depends. On most operating systems with native <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> support the <code>localhost<\/code> is pretty safe bet. However, it is better to ask <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> what is the proper one to use by calling <code>client.getHost()<\/code>, it may not return <code>localhost<\/code> all the time (like in case of <a href=\"https:\/\/github.com\/boot2docker\/boot2docker\">boot2docker<\/a> for example).<\/p>\n<pre class=\"brush:java\">final String url = String.format(\n    \"jdbc:mysql:\/\/%s:%d\/my_app_db?user=root&amp;password=p$ssw0rd&amp;verifyServerCertificate=false\", \n        client.getHost(), port);\n\ntry (Connection connection = DriverManager.getConnection(url)) {\n\ttry(ResultSet results = connection.getMetaData().getCatalogs()) {\n\t\twhile (results.next()) {\n\t\t\tLOG.info(\"Found catalog '{}'\", results.getString(1));\n\t\t}\t\n\t}\n} catch (SQLException ex) {\n\tLOG.error(\"MySQL connection problem\", ex);\n}\n<\/pre>\n<p>Awesome, if everything works perfectly fine (as it should), you would see something like that in the console output (please notice that our database <code>my_app_db<\/code> is also here):<\/p>\n<pre class=\"brush:bash\">...\n10:25:19.439 [main] INFO  Found catalog 'information_schema'\n10:25:19.439 [main] INFO  Found catalog 'my_app_db'\n10:25:19.439 [main] INFO  Found catalog 'mysql'\n10:25:19.439 [main] INFO  Found catalog 'performance_schema'\n10:25:19.439 [main] INFO  Found catalog 'sys'\n<\/pre>\n<p>Last but not least, please do not forget to terminate the container and close the <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> instance at the very end.<\/p>\n<pre class=\"brush:java\">\/\/ Stop the container\nclient.stopContainer(container.id(), 5);\nclient.removeContainer(container.id());\t\t\nclient.close();\n<\/pre>\n<p>And we are done! Could you accomplish all that using just <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Docker Engine API<\/a> directly? Definitely, but the amount of supporting Java code you would need to write for that would unpleasantly surprise you.<\/p>\n<h2><a name=\"conclusions\"><\/a>11. Conclusions<\/h2>\n<p>Fairly speaking, <a href=\"https:\/\/docs.docker.com\/engine\/api\/\">Docker Engine API<\/a> is just a set of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Representational_state_transfer\">REST(ful) services<\/a>, very rich ones though. For sure, anyone could access them from any Java application just by using generic HTTP client. So what the deal?<\/p>\n<p>Personally, I would emphasize the explicit contracts, maintainability and type-safety. Instead of dealing with URIs, query or path parameters, raw <a href=\"http:\/\/www.json.org\/\">JSON<\/a> for request and response payloads, you have a stable Java-based API, with strict set of capabilities and expectations.<\/p>\n<p>As we are going to see later on, <a href=\"https:\/\/github.com\/spotify\/docker-client\">Spotify Docker Client<\/a> serves as a solid foundation for automation and advanced testing techniques, particularly in context of Java development. Thank you, <a href=\"https:\/\/spotify.github.io\/\">Spotify<\/a>, for giving back to the community!<\/p>\n<h2><a name=\"next\"><\/a>12. What\u2019s next<\/h2>\n<p>In the next section we are going to talk about the ways to migrate your build pipelines to <a href=\"https:\/\/www.docker.com\/\">Docker<\/a>, as such eliminating the need to replicate and maintain the tooling on each host or in every environment.<\/p>\n<p>The complete project sources are <a href=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2017\/10\/docker-http-client.zip\">available for download<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article is part of our Academy Course titled Docker Tutorial for Java Developers. In this course, we provide a series of tutorials so that you can develop your own Docker based applications. We cover a wide range of topics, from Docker over command line, to development, testing, deployment and continuous integration. With our straightforward &hellip;<\/p>\n","protected":false},"author":141,"featured_media":24013,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14],"tags":[936],"class_list":["post-69790","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","tag-docker"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Docker for Java Developers: Docker over HTTP\/REST - Java Code Geeks<\/title>\n<meta name=\"description\" content=\"This article is part of our Academy Course titled Docker Tutorial for Java Developers. In this course, we provide a series of tutorials so that you can\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Docker for Java Developers: Docker over HTTP\/REST - Java Code Geeks\" \/>\n<meta property=\"og:description\" content=\"This article is part of our Academy Course titled Docker Tutorial for Java Developers. In this course, we provide a series of tutorials so that you can\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html\" \/>\n<meta property=\"og:site_name\" content=\"Java Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/javacodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2017-10-23T06:47:03+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-12-11T08:26:48+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2014\/04\/docker-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Andrey Redko\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andrey Redko\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html\"},\"author\":{\"name\":\"Andrey Redko\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/771a6504862edc45322776832cbce413\"},\"headline\":\"Docker for Java Developers: Docker over HTTP\\\/REST\",\"datePublished\":\"2017-10-23T06:47:03+00:00\",\"dateModified\":\"2023-12-11T08:26:48+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html\"},\"wordCount\":2180,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2014\\\/04\\\/docker-logo.jpg\",\"keywords\":[\"Docker\"],\"articleSection\":[\"DevOps\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html\",\"name\":\"Docker for Java Developers: Docker over HTTP\\\/REST - Java Code Geeks\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2014\\\/04\\\/docker-logo.jpg\",\"datePublished\":\"2017-10-23T06:47:03+00:00\",\"dateModified\":\"2023-12-11T08:26:48+00:00\",\"description\":\"This article is part of our Academy Course titled Docker Tutorial for Java Developers. In this course, we provide a series of tutorials so that you can\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html#primaryimage\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2014\\\/04\\\/docker-logo.jpg\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2014\\\/04\\\/docker-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/10\\\/docker-java-developers-docker-http-rest.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"DevOps\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/devops\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Docker for Java Developers: Docker over HTTP\\\/REST\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"name\":\"Java Code Geeks\",\"description\":\"Java Developers Resource Center\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"alternateName\":\"JCG\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.javacodegeeks.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/javacodegeeks\",\"https:\\\/\\\/x.com\\\/javacodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/771a6504862edc45322776832cbce413\",\"name\":\"Andrey Redko\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g\",\"caption\":\"Andrey Redko\"},\"description\":\"Andriy is a well-grounded software developer with more then 12 years of practical experience using Java\\\/EE, C#\\\/.NET, C++, Groovy, Ruby, functional programming (Scala), databases (MySQL, PostgreSQL, Oracle) and NoSQL solutions (MongoDB, Redis).\",\"sameAs\":[\"http:\\\/\\\/aredko.blogspot.com\\\/\",\"http:\\\/\\\/ca.linkedin.com\\\/in\\\/aredko\"],\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/author\\\/andrey-redko\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Docker for Java Developers: Docker over HTTP\/REST - Java Code Geeks","description":"This article is part of our Academy Course titled Docker Tutorial for Java Developers. In this course, we provide a series of tutorials so that you can","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html","og_locale":"en_US","og_type":"article","og_title":"Docker for Java Developers: Docker over HTTP\/REST - Java Code Geeks","og_description":"This article is part of our Academy Course titled Docker Tutorial for Java Developers. In this course, we provide a series of tutorials so that you can","og_url":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html","og_site_name":"Java Code Geeks","article_publisher":"https:\/\/www.facebook.com\/javacodegeeks","article_published_time":"2017-10-23T06:47:03+00:00","article_modified_time":"2023-12-11T08:26:48+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2014\/04\/docker-logo.jpg","type":"image\/jpeg"}],"author":"Andrey Redko","twitter_card":"summary_large_image","twitter_creator":"@javacodegeeks","twitter_site":"@javacodegeeks","twitter_misc":{"Written by":"Andrey Redko","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html#article","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html"},"author":{"name":"Andrey Redko","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/771a6504862edc45322776832cbce413"},"headline":"Docker for Java Developers: Docker over HTTP\/REST","datePublished":"2017-10-23T06:47:03+00:00","dateModified":"2023-12-11T08:26:48+00:00","mainEntityOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html"},"wordCount":2180,"commentCount":0,"publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2014\/04\/docker-logo.jpg","keywords":["Docker"],"articleSection":["DevOps"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html","url":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html","name":"Docker for Java Developers: Docker over HTTP\/REST - Java Code Geeks","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html#primaryimage"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2014\/04\/docker-logo.jpg","datePublished":"2017-10-23T06:47:03+00:00","dateModified":"2023-12-11T08:26:48+00:00","description":"This article is part of our Academy Course titled Docker Tutorial for Java Developers. In this course, we provide a series of tutorials so that you can","breadcrumb":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html#primaryimage","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2014\/04\/docker-logo.jpg","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2014\/04\/docker-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.javacodegeeks.com\/2017\/10\/docker-java-developers-docker-http-rest.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.javacodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"DevOps","item":"https:\/\/www.javacodegeeks.com\/category\/devops"},{"@type":"ListItem","position":3,"name":"Docker for Java Developers: Docker over HTTP\/REST"}]},{"@type":"WebSite","@id":"https:\/\/www.javacodegeeks.com\/#website","url":"https:\/\/www.javacodegeeks.com\/","name":"Java Code Geeks","description":"Java Developers Resource Center","publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"alternateName":"JCG","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.javacodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.javacodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.javacodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/javacodegeeks","https:\/\/x.com\/javacodegeeks"]},{"@type":"Person","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/771a6504862edc45322776832cbce413","name":"Andrey Redko","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g","caption":"Andrey Redko"},"description":"Andriy is a well-grounded software developer with more then 12 years of practical experience using Java\/EE, C#\/.NET, C++, Groovy, Ruby, functional programming (Scala), databases (MySQL, PostgreSQL, Oracle) and NoSQL solutions (MongoDB, Redis).","sameAs":["http:\/\/aredko.blogspot.com\/","http:\/\/ca.linkedin.com\/in\/aredko"],"url":"https:\/\/www.javacodegeeks.com\/author\/andrey-redko"}]}},"_links":{"self":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/69790","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/users\/141"}],"replies":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/comments?post=69790"}],"version-history":[{"count":0,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/69790\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media\/24013"}],"wp:attachment":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media?parent=69790"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/categories?post=69790"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/tags?post=69790"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}