diff --git a/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java b/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java index ac98db2f9..2ab9e841d 100644 --- a/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.InputStream; import java.net.URI; +import java.util.List; import java.util.Map; import javax.annotation.CheckForNull; @@ -34,10 +35,18 @@ public interface BuildImageCmd extends AsyncDockerCmd getTags(); + /** * "remote" in API */ @@ -109,8 +118,14 @@ public interface BuildImageCmd extends AsyncDockerCmd tags); + BuildImageCmd withRemote(URI remote); BuildImageCmd withBaseDirectory(File baseDirectory); diff --git a/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java index c34b05155..6f91a9cd9 100644 --- a/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java @@ -7,6 +7,7 @@ import java.io.InputStream; import java.net.URI; import java.util.HashMap; +import java.util.List; import java.util.Map; import com.github.dockerjava.api.command.BuildImageCmd; @@ -15,6 +16,8 @@ import com.github.dockerjava.core.dockerfile.Dockerfile; import com.github.dockerjava.core.util.FilePathUtil; +import javax.annotation.CheckForNull; + /** * * Build an image from Dockerfile. @@ -24,8 +27,11 @@ public class BuildImageCmdImpl extends AbstrAsyncDockerCmd tags; + private Boolean noCache; private Boolean remove = true; @@ -82,11 +88,17 @@ public BuildImageCmdImpl(BuildImageCmd.Exec exec, InputStream tarInputStream) { // getters API + @Deprecated @Override public String getTag() { return tag; } + @CheckForNull + public List getTags() { + return tags; + } + @Override public URI getRemote() { return remote; @@ -178,6 +190,10 @@ public Long getShmsize() { // setters + /** + * @deprecated use #withTags() + */ + @Deprecated @Override public BuildImageCmdImpl withTag(String tag) { checkNotNull(tag, "Tag is null"); @@ -185,6 +201,12 @@ public BuildImageCmdImpl withTag(String tag) { return this; } + @Override + public BuildImageCmdImpl withTags(List tags) { + this.tags = tags; + return this; + } + @Override public BuildImageCmd withRemote(URI remote) { this.remote = remote; diff --git a/src/main/java/com/github/dockerjava/netty/WebTarget.java b/src/main/java/com/github/dockerjava/netty/WebTarget.java index ef1510f5c..883f00601 100644 --- a/src/main/java/com/github/dockerjava/netty/WebTarget.java +++ b/src/main/java/com/github/dockerjava/netty/WebTarget.java @@ -1,41 +1,56 @@ package com.github.dockerjava.netty; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.handler.codec.http.HttpConstants; import org.apache.commons.lang.StringUtils; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /** - * This class is basically a replacement of javax.ws.rs.client.WebTarget to allow simpler migration of JAX-RS code to a netty based + * This class is basically a replacement of {@link javax.ws.rs.client.WebTarget} to allow simpler migration of JAX-RS code to a netty based * implementation. * * @author Marcus Linke */ public class WebTarget { + private static final ObjectMapper MAPPER = new ObjectMapper(); private final ChannelProvider channelProvider; private final ImmutableList path; private final ImmutableMap queryParams; + /** + * Multiple values for the same name param. + */ + private final ImmutableMap> queryParamsList; private static final String PATH_SEPARATOR = "/"; public WebTarget(ChannelProvider channelProvider) { - this(channelProvider, ImmutableList.of(), ImmutableMap.of()); + this(channelProvider, ImmutableList.of(), ImmutableMap.of(), + ImmutableMap.>of()); } private WebTarget(ChannelProvider channelProvider, ImmutableList path, - ImmutableMap queryParams) { + ImmutableMap queryParams, + ImmutableMap> queryParamsList) { this.channelProvider = channelProvider; this.path = path; this.queryParams = queryParams; + this.queryParamsList = queryParamsList; } public WebTarget path(String... components) { @@ -45,7 +60,7 @@ public WebTarget path(String... components) { newPath.addAll(Arrays.asList(StringUtils.split(component, PATH_SEPARATOR))); } - return new WebTarget(channelProvider, newPath.build(), queryParams); + return new WebTarget(channelProvider, newPath.build(), queryParams, queryParamsList); } public InvocationBuilder request() { @@ -53,7 +68,13 @@ public InvocationBuilder request() { List params = new ArrayList<>(); for (Map.Entry entry : queryParams.entrySet()) { - params.add(entry.getKey() + "=" + entry.getValue()); + params.add(entry.getKey() + "=" + encodeComponent(entry.getValue(), HttpConstants.DEFAULT_CHARSET)); + } + + for (Map.Entry> entry : queryParamsList.entrySet()) { + for (String entryValueValue : entry.getValue()) { + params.add(entry.getKey() + "=" + encodeComponent(entryValueValue, HttpConstants.DEFAULT_CHARSET)); + } } if (!params.isEmpty()) { @@ -63,13 +84,25 @@ public InvocationBuilder request() { return new InvocationBuilder(channelProvider, resource); } + /** + * @see io.netty.handler.codec.http.QueryStringEncoder + */ + private static String encodeComponent(String s, Charset charset) { + // TODO: Optimize me. + try { + return URLEncoder.encode(s, charset.name()).replace("+", "%20"); + } catch (UnsupportedEncodingException ignored) { + throw new UnsupportedCharsetException(charset.name()); + } + } + public WebTarget resolveTemplate(String name, Object value) { ImmutableList.Builder newPath = ImmutableList.builder(); for (String component : path) { component = component.replaceAll("\\{" + name + "\\}", value.toString()); newPath.add(component); } - return new WebTarget(channelProvider, newPath.build(), queryParams); + return new WebTarget(channelProvider, newPath.build(), queryParams, queryParamsList); } public WebTarget queryParam(String name, Object value) { @@ -77,7 +110,32 @@ public WebTarget queryParam(String name, Object value) { if (value != null) { builder.put(name, value.toString()); } - return new WebTarget(channelProvider, path, builder.build()); + return new WebTarget(channelProvider, path, builder.build(), queryParamsList); + } + + public WebTarget queryParamsList(String name, List values) { + ImmutableMap.Builder> builder = ImmutableMap.>builder().putAll(queryParamsList); + if (values != null) { + ImmutableList.Builder valueBuilder = ImmutableList.builder(); + for (Object value : values) { + valueBuilder.add(value.toString()); + } + builder.put(name, valueBuilder.build()); + } + return new WebTarget(channelProvider, path, queryParams, builder.build()); + } + + public WebTarget queryParamsJsonMap(String name, Map values) { + if (values != null && !values.isEmpty()) { + try { + // when param value is JSON string + return queryParam(name, MAPPER.writeValueAsString(values)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + return this; + } } @Override @@ -97,7 +155,14 @@ public boolean equals(Object o) { if (path != null ? !path.equals(webTarget.path) : webTarget.path != null) { return false; } - return queryParams != null ? queryParams.equals(webTarget.queryParams) : webTarget.queryParams == null; + if (queryParams != null ? !queryParams.equals(webTarget.queryParams) : webTarget.queryParams != null) { + return false; + } + if (queryParamsList != null ? !queryParamsList.equals(webTarget.queryParamsList) : webTarget.queryParamsList != null) { + return false; + } + + return true; } @Override @@ -105,6 +170,7 @@ public int hashCode() { int result = channelProvider != null ? channelProvider.hashCode() : 0; result = 31 * result + (path != null ? path.hashCode() : 0); result = 31 * result + (queryParams != null ? queryParams.hashCode() : 0); + result = 31 * result + (queryParamsList != null ? queryParamsList.hashCode() : 0); return result; } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java index 43152570e..6e2cfde6b 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java @@ -21,8 +21,6 @@ public class BuildImageCmdExec extends AbstrAsyncDockerCmdExec if (command.getTag() != null) { webTarget = webTarget.queryParam("t", command.getTag()); } + if (command.getTags() != null) { + webTarget = webTarget.queryParamsList("t", command.getTags()); + } if (command.getRemote() != null) { webTarget = webTarget.queryParam("remote", command.getRemote().toString()); } @@ -86,13 +87,17 @@ protected Void execute0(BuildImageCmd command, ResultCallback webTarget = webTarget.queryParam("cpusetcpus", command.getCpusetcpus()); } - webTarget = writeMap(webTarget, "buildargs", command.getBuildArgs()); + if (command.getBuildArgs() != null) { + webTarget = webTarget.queryParamsJsonMap("buildargs", command.getBuildArgs()); + } if (command.getShmsize() != null) { webTarget = webTarget.queryParam("shmsize", command.getShmsize()); } - webTarget = writeMap(webTarget, "labels", command.getLabels()); + if (command.getLabels() != null) { + webTarget = webTarget.queryParamsJsonMap("labels", command.getLabels()); + } LOGGER.trace("POST: {}", webTarget); @@ -106,16 +111,4 @@ protected Void execute0(BuildImageCmd command, ResultCallback return null; } - - private WebTarget writeMap(WebTarget webTarget, String name, Map value) { - if (value != null && !value.isEmpty()) { - try { - return webTarget.queryParam(name, MAPPER.writeValueAsString(value)); - } catch (IOException e) { - throw new RuntimeException(e); - } - } else { - return webTarget; - } - } }