diff --git a/src/main/java/com/github/dockerjava/netty/WebTarget.java b/src/main/java/com/github/dockerjava/netty/WebTarget.java index 090fea67c..ef1510f5c 100644 --- a/src/main/java/com/github/dockerjava/netty/WebTarget.java +++ b/src/main/java/com/github/dockerjava/netty/WebTarget.java @@ -2,12 +2,14 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; 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 * implementation. @@ -16,32 +18,40 @@ */ public class WebTarget { - private ChannelProvider channelProvider; + private final ChannelProvider channelProvider; - private List path = new ArrayList(); + private final ImmutableList path; - private Map queryParams = new HashMap(); + private final ImmutableMap queryParams; private static final String PATH_SEPARATOR = "/"; public WebTarget(ChannelProvider channelProvider) { + this(channelProvider, ImmutableList.of(), ImmutableMap.of()); + } + + private WebTarget(ChannelProvider channelProvider, + ImmutableList path, + ImmutableMap queryParams) { this.channelProvider = channelProvider; + this.path = path; + this.queryParams = queryParams; } public WebTarget path(String... components) { + ImmutableList.Builder newPath = ImmutableList.builder().addAll(this.path); for (String component : components) { - - path.addAll(Arrays.asList(StringUtils.split(component, PATH_SEPARATOR))); + newPath.addAll(Arrays.asList(StringUtils.split(component, PATH_SEPARATOR))); } - return this; + return new WebTarget(channelProvider, newPath.build(), queryParams); } public InvocationBuilder request() { String resource = PATH_SEPARATOR + StringUtils.join(path, PATH_SEPARATOR); - List params = new ArrayList(); + List params = new ArrayList<>(); for (Map.Entry entry : queryParams.entrySet()) { params.add(entry.getKey() + "=" + entry.getValue()); } @@ -54,20 +64,47 @@ public InvocationBuilder request() { } public WebTarget resolveTemplate(String name, Object value) { - List newPath = new ArrayList(); + ImmutableList.Builder newPath = ImmutableList.builder(); for (String component : path) { component = component.replaceAll("\\{" + name + "\\}", value.toString()); newPath.add(component); } - path = newPath; - return this; + return new WebTarget(channelProvider, newPath.build(), queryParams); } public WebTarget queryParam(String name, Object value) { + ImmutableMap.Builder builder = ImmutableMap.builder().putAll(queryParams); if (value != null) { - queryParams.put(name, value.toString()); + builder.put(name, value.toString()); } - return this; + return new WebTarget(channelProvider, path, builder.build()); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + WebTarget webTarget = (WebTarget) o; + + if (channelProvider != null ? !channelProvider.equals(webTarget.channelProvider) : webTarget.channelProvider != null) { + return false; + } + if (path != null ? !path.equals(webTarget.path) : webTarget.path != null) { + return false; + } + return queryParams != null ? queryParams.equals(webTarget.queryParams) : webTarget.queryParams == null; + } + + @Override + 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); + return result; + } } diff --git a/src/test/java/com/github/dockerjava/netty/WebTargetTest.java b/src/test/java/com/github/dockerjava/netty/WebTargetTest.java new file mode 100644 index 000000000..a462eb8b5 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/WebTargetTest.java @@ -0,0 +1,39 @@ +package com.github.dockerjava.netty; + +import static org.testng.Assert.assertEquals; + +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Alexander Koshevoy + */ +public class WebTargetTest { + @Mock private ChannelProvider channelProvider; + + @BeforeMethod + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void verifyImmutability() throws Exception { + WebTarget emptyWebTarget = new WebTarget(channelProvider); + + WebTarget initWebTarget = emptyWebTarget.path("/containers/{id}/attach").resolveTemplate("id", "d03da378b592") + .queryParam("logs", "true"); + + WebTarget anotherWebTarget = emptyWebTarget.path("/containers/{id}/attach") + .resolveTemplate("id", "2cfada4e3c07").queryParam("stdin", "true"); + + assertEquals(new WebTarget(channelProvider), emptyWebTarget); + + assertEquals(new WebTarget(channelProvider).path("/containers/d03da378b592/attach") + .queryParam("logs", "true"), initWebTarget); + + assertEquals(new WebTarget(channelProvider).path("/containers/2cfada4e3c07/attach") + .queryParam("stdin", "true"), anotherWebTarget); + } +}