Communication Between Linked Docker Containers With Java EE 📎
Docker container to container communication requires either the use of legacy docker links or links in user defined frameworks.
The IP address is dynamically assigned by the docker daemon to the containers and is not known at build time. In a legacy-link scenario the IP address and port-number has to be extracted from static OS environment variables, and in the user-defined networks case the container name, or its alias, can be directly used as hostname. Also in the user-defined scenario there is no additional port-lookup needed. The exposed port can be used without any translation.
For the extraction of legacy links the IP address and the port have to be resolved using System#getenv
:
public interface URIProvider {
public static String computeURIWithEnvironmentEntries(String linkName, int portNumber) {
String stringifiedPort = String.valueOf(portNumber);
String portKey = computePortKey(linkName, stringifiedPort);
String addressKey = computeAddressKey(linkName, stringifiedPort);
return "http://" + getEnvironmentVariable(addressKey) + ":" + getEnvironmentVariable(portKey);
}
static String getEnvironmentVariable(String key) {
String variable = System.getenv(key);
if (variable == null) {
throw new IllegalStateException("No environment variable found for: " + key);
}
return variable;
}
static String computeKeyPrefix(String linkName, String portNumber) {
String upperLink = linkName.toUpperCase();
return upperLink + "_PORT_" + portNumber + "_TCP_";
}
static String computePortKey(String linkName, String portNumber) {
return computeKeyPrefix(linkName, portNumber) + "PORT";
}
static String computeAddressKey(String linkName, String portNumber) {
return computeKeyPrefix(linkName, portNumber) + "ADDR";
}
}
In the user-defined network scenario the link and port can be directly used:
public static String computeUri(String linkName, int portNumber, String resource) {
return "http://" + linkName + ":" + portNumber + resource;
}
public static String computeUri(String linkName, int portNumber) {
return computeUri(linkName, portNumber, "");
}
In Java EE context we can introduce more convenience with a qualifier:
@Qualifier
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LegacyLink {
@Nonbinding
String name() default "";
@Nonbinding
int portNumber() default 8080;
@Nonbinding
String path() default "";
}
...and combine it with some conventions:
@Produces
@LegacyLink
public String exposeLegacyLink(InjectionPoint ip) {
Annotated field = ip.getAnnotated();
LegacyLink link = field.getAnnotation(LegacyLink.class);
String linkName = link.name();
if (linkName.isEmpty()) {
linkName = ip.getMember().getName().toUpperCase();
}
int portNumber = link.portNumber();
String resource = link.path();
if (resource.isEmpty()) {
return URIProvider.computeURIWithEnvironmentEntries(linkName, portNumber);
} else {
return URIProvider.computeURIWithEnvironmentEntries(linkName, portNumber, resource);
}
}
@Produces
@LegacyLink
public WebTarget exposeLegacyLinkWebTarget(InjectionPoint ip) {
Client client = ClientBuilder.newClient();
return client.target(exposeLegacyLink(ip));
}
...what makes the WebTarget
conveniently injectable:
@Inject
@LegacyLink(portNumber = 8080, path = "/hugo")
String ping;
The servicelink
utility is already available in maven-central:
<dependency>
<groupId>com.airhacks</groupId>
<artifactId>servicelink</artifactId>
<version>[RECENT]</version>
</dependency>
See also: github.com/AdamBien/servicelink for documentation.
I got the idea for this utility in an undisclosed location -- the origin name was: urimator :-).
See you at Java EE Workshops at Munich Airport, Terminal 2 or Virtual Dedicated Workshops / consulting. Is Munich's airport too far? Learn from home: airhacks.io.