[JAVA-29427] Consolidate libraries modules (#15536)
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
package com.baeldung.jnats;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.nats.client.AsyncSubscription;
|
||||
import io.nats.client.Connection;
|
||||
import io.nats.client.Message;
|
||||
import io.nats.client.Nats;
|
||||
import io.nats.client.Options;
|
||||
import io.nats.client.Subscription;
|
||||
import io.nats.client.SyncSubscription;
|
||||
|
||||
public final class NatsClient {
|
||||
|
||||
private final String serverURI;
|
||||
|
||||
private final Connection natsConnection;
|
||||
|
||||
private final Map<String, Subscription> subscriptions = new HashMap<>();
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(NatsClient.class);
|
||||
|
||||
NatsClient() {
|
||||
this.serverURI = "jnats://localhost:4222";
|
||||
natsConnection = initConnection(serverURI);
|
||||
}
|
||||
|
||||
public NatsClient(String serverURI) {
|
||||
if ((serverURI != null) && (!serverURI.isEmpty())) {
|
||||
this.serverURI = serverURI;
|
||||
} else {
|
||||
this.serverURI = "jnats://localhost:4222";
|
||||
}
|
||||
|
||||
natsConnection = initConnection(serverURI);
|
||||
}
|
||||
|
||||
public void closeConnection() {
|
||||
// Close connection
|
||||
natsConnection.close();
|
||||
}
|
||||
|
||||
private Connection initConnection(String uri) {
|
||||
try {
|
||||
Options options = new Options.Builder()
|
||||
.errorCb(ex -> log.error("Connection Exception: ", ex))
|
||||
.disconnectedCb(event -> log.error("Channel disconnected: {}", event.getConnection()))
|
||||
.reconnectedCb(event -> log.error("Reconnected to server: {}", event.getConnection()))
|
||||
.build();
|
||||
|
||||
return Nats.connect(uri, options);
|
||||
} catch (IOException ioe) {
|
||||
log.error("Error connecting to NATs! ", ioe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void publishMessage(String topic, String replyTo, String message) {
|
||||
try {
|
||||
natsConnection.publish(topic, replyTo, message.getBytes());
|
||||
} catch (IOException ioe) {
|
||||
log.error("Error publishing message: {} to {} ", message, topic, ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public void subscribeAsync(String topic) {
|
||||
|
||||
AsyncSubscription subscription = natsConnection.subscribe(
|
||||
topic, msg -> log.info("Received message on {}", msg.getSubject()));
|
||||
|
||||
if (subscription == null) {
|
||||
log.error("Error subscribing to {}", topic);
|
||||
} else {
|
||||
subscriptions.put(topic, subscription);
|
||||
}
|
||||
}
|
||||
|
||||
SyncSubscription subscribeSync(String topic) {
|
||||
return natsConnection.subscribe(topic);
|
||||
}
|
||||
|
||||
public void unsubscribe(String topic) {
|
||||
try {
|
||||
Subscription subscription = subscriptions.get(topic);
|
||||
|
||||
if (subscription != null) {
|
||||
subscription.unsubscribe();
|
||||
} else {
|
||||
log.error("{} not found. Unable to unsubscribe.", topic);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
log.error("Error unsubscribing from {} ", topic, ioe);
|
||||
}
|
||||
}
|
||||
|
||||
Message makeRequest(String topic, String request) {
|
||||
try {
|
||||
return natsConnection.request(topic, request.getBytes(), 100);
|
||||
} catch (IOException | InterruptedException ioe) {
|
||||
log.error("Error making request {} to {} ", topic, request, ioe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void installReply(String topic, String reply) {
|
||||
natsConnection.subscribe(topic, message -> {
|
||||
try {
|
||||
natsConnection.publish(message.getReplyTo(), reply.getBytes());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SyncSubscription joinQueueGroup(String topic, String queue) {
|
||||
return natsConnection.subscribe(topic, queue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.baeldung.dockerapi;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.hamcrest.core.Is;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.CreateContainerResponse;
|
||||
import com.github.dockerjava.api.command.InspectContainerResponse;
|
||||
import com.github.dockerjava.api.model.Container;
|
||||
import com.github.dockerjava.api.model.PortBinding;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
|
||||
public class ContainerLiveTest {
|
||||
|
||||
private static DockerClient dockerClient;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
dockerClient = DockerClientBuilder.getInstance().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingRunningContainers_thenReturnNonEmptyList() {
|
||||
|
||||
// when
|
||||
List<Container> containers = dockerClient.listContainersCmd().exec();
|
||||
|
||||
// then
|
||||
assertThat(containers.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingExitedContainers_thenReturnNonEmptyList() {
|
||||
|
||||
// when
|
||||
List<Container> containers = dockerClient.listContainersCmd().withShowSize(true).withShowAll(true).withStatusFilter("exited").exec();
|
||||
|
||||
// then
|
||||
assertThat(containers.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingContainer_thenMustReturnContainerId() {
|
||||
|
||||
// when
|
||||
CreateContainerResponse container = dockerClient.createContainerCmd("mongo:3.6").withCmd("--bind_ip_all").withName("mongo").withHostName("baeldung").withEnv("MONGO_LATEST_VERSION=3.6").withPortBindings(PortBinding.parse("9999:27017")).exec();
|
||||
|
||||
// then
|
||||
MatcherAssert.assertThat(container.getId(), is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenHavingContainer_thenRunContainer() throws InterruptedException {
|
||||
|
||||
// when
|
||||
CreateContainerResponse container = dockerClient.createContainerCmd("alpine:3.6").withCmd("sleep", "10000").exec();
|
||||
|
||||
Thread.sleep(3000);
|
||||
// then
|
||||
dockerClient.startContainerCmd(container.getId()).exec();
|
||||
|
||||
dockerClient.stopContainerCmd(container.getId()).exec();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRunningContainer_thenStopContainer() throws InterruptedException {
|
||||
|
||||
// when
|
||||
CreateContainerResponse container = dockerClient.createContainerCmd("alpine:3.6").withCmd("sleep", "10000").exec();
|
||||
|
||||
Thread.sleep(3000);
|
||||
dockerClient.startContainerCmd(container.getId()).exec();
|
||||
|
||||
// then
|
||||
dockerClient.stopContainerCmd(container.getId()).exec();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRunningContainer_thenKillContainer() throws InterruptedException {
|
||||
|
||||
// when
|
||||
CreateContainerResponse container = dockerClient.createContainerCmd("alpine:3.6").withCmd("sleep", "10000").exec();
|
||||
|
||||
dockerClient.startContainerCmd(container.getId()).exec();
|
||||
|
||||
Thread.sleep(3000);
|
||||
dockerClient.stopContainerCmd(container.getId()).exec();
|
||||
|
||||
// then
|
||||
dockerClient.killContainerCmd(container.getId()).exec();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenHavingContainer_thenInspectContainer() {
|
||||
|
||||
// when
|
||||
CreateContainerResponse container = dockerClient.createContainerCmd("alpine:3.6").withCmd("sleep", "10000").exec();
|
||||
|
||||
// then
|
||||
InspectContainerResponse containerResponse = dockerClient.inspectContainerCmd(container.getId()).exec();
|
||||
|
||||
MatcherAssert.assertThat(containerResponse.getId(), Is.is(container.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenContainer_whenCommittingContainer_thenMustReturnImageId() {
|
||||
|
||||
// given
|
||||
CreateContainerResponse container = dockerClient.createContainerCmd("alpine:3.6").withCmd("sleep", "10000").exec();
|
||||
|
||||
// when
|
||||
String imageId = dockerClient.commitCmd(container.getId()).withEnv("SNAPSHOT_YEAR=2018").withMessage("add git support").withCmd("sleep", "10000").withRepository("alpine").withTag("3.6.v2").exec();
|
||||
|
||||
// then
|
||||
assertThat(imageId, is(not(null)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.baeldung.dockerapi;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
|
||||
public class DockerClientLiveTest {
|
||||
|
||||
@Test
|
||||
public void whenCreatingDockerClient_thenReturnDefaultInstance() {
|
||||
|
||||
// when
|
||||
DefaultDockerClientConfig.Builder config = DefaultDockerClientConfig.createDefaultConfigBuilder();
|
||||
DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
|
||||
|
||||
// then
|
||||
assertNotNull(dockerClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingDockerClientWithDockerHost_thenReturnInstance() {
|
||||
// when
|
||||
DockerClient dockerClient = DockerClientBuilder.getInstance("tcp://docker.bealdung.com:2375").build();
|
||||
|
||||
// then
|
||||
assertNotNull(dockerClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingAdvanceDockerClient_thenReturnInstance() {
|
||||
|
||||
// when
|
||||
DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().withRegistryEmail("info@bealdung.com").withRegistryUrl("register.bealdung.io/v2/").withRegistryPassword("strongpassword").withRegistryUsername("bealdung")
|
||||
.withDockerCertPath("/home/bealdung/public/.docker/certs").withDockerConfig("/home/bealdung/public/.docker/").withDockerTlsVerify("1").withDockerHost("tcp://docker.beauldung.com:2376").build();
|
||||
|
||||
DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
|
||||
|
||||
// then
|
||||
assertNotNull(dockerClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingDockerClientWithProperties_thenReturnInstance() {
|
||||
|
||||
// when
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("registry.email", "info@bealdung.com");
|
||||
properties.setProperty("registry.url", "register.bealdung.io/v2/");
|
||||
properties.setProperty("registry.password", "strongpassword");
|
||||
properties.setProperty("registry.username", "bealdung");
|
||||
properties.setProperty("DOCKER_CERT_PATH", "/home/bealdung/public/.docker/certs");
|
||||
properties.setProperty("DOCKER_CONFIG", "/home/bealdung/public/.docker/");
|
||||
properties.setProperty("DOCKER_TLS_VERIFY", "1");
|
||||
properties.setProperty("DOCKER_HOST", "tcp://docker.bealdung.com:2376");
|
||||
|
||||
DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().withProperties(properties).build();
|
||||
|
||||
DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
|
||||
|
||||
// then
|
||||
assertNotNull(dockerClient);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.baeldung.dockerapi;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.hamcrest.core.Is;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.InspectImageResponse;
|
||||
import com.github.dockerjava.api.model.Image;
|
||||
import com.github.dockerjava.api.model.SearchItem;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
import com.github.dockerjava.core.command.BuildImageResultCallback;
|
||||
import com.github.dockerjava.core.command.PullImageResultCallback;
|
||||
import com.github.dockerjava.core.command.PushImageResultCallback;
|
||||
|
||||
public class ImageLiveTest {
|
||||
|
||||
private static DockerClient dockerClient;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
dockerClient = DockerClientBuilder.getInstance().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingImages_thenReturnNonEmptyList() {
|
||||
|
||||
// when
|
||||
List<Image> images = dockerClient.listImagesCmd().exec();
|
||||
|
||||
// then
|
||||
assertThat(images.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingImagesWithIntermediateImages_thenReturnNonEmptyList() {
|
||||
|
||||
// when
|
||||
List<Image> images = dockerClient.listImagesCmd().withShowAll(true).exec();
|
||||
|
||||
// then
|
||||
assertThat(images.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingDanglingImages_thenReturnNonNullList() {
|
||||
|
||||
// when
|
||||
List<Image> images = dockerClient.listImagesCmd().withDanglingFilter(true).exec();
|
||||
|
||||
// then
|
||||
assertThat(images, is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenBuildingImage_thenMustReturnImageId() {
|
||||
|
||||
// when
|
||||
String imageId = dockerClient.buildImageCmd().withDockerfile(new File("src/test/resources/dockerapi/Dockerfile")).withPull(true).withNoCache(true).withTag("alpine:git").exec(new BuildImageResultCallback()).awaitImageId();
|
||||
|
||||
// then
|
||||
assertThat(imageId, is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenListOfImages_whenInspectImage_thenMustReturnObject() {
|
||||
|
||||
// given
|
||||
List<Image> images = dockerClient.listImagesCmd().exec();
|
||||
Image image = images.get(0);
|
||||
|
||||
// when
|
||||
InspectImageResponse imageResponse = dockerClient.inspectImageCmd(image.getId()).exec();
|
||||
|
||||
// then
|
||||
MatcherAssert.assertThat(imageResponse.getId(), Is.is(image.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenListOfImages_whenTagImage_thenListMustIncrement() {
|
||||
|
||||
// given
|
||||
List<Image> images = dockerClient.listImagesCmd().exec();
|
||||
Image image = images.get(0);
|
||||
|
||||
// when
|
||||
dockerClient.tagImageCmd(image.getId(), "baeldung/alpine", "3.6.v2").exec();
|
||||
|
||||
// then
|
||||
List<Image> imagesNow = dockerClient.listImagesCmd().exec();
|
||||
assertThat(imagesNow.size(), is(greaterThan(images.size())));
|
||||
}
|
||||
|
||||
public void pushingAnImage() throws InterruptedException {
|
||||
|
||||
dockerClient.pushImageCmd("baeldung/alpine").withTag("3.6.v2").exec(new PushImageResultCallback()).awaitCompletion(90, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenPullingImage_thenImageListNotEmpty() throws InterruptedException {
|
||||
|
||||
// when
|
||||
dockerClient.pullImageCmd("alpine").withTag("latest").exec(new PullImageResultCallback()).awaitCompletion(30, TimeUnit.SECONDS);
|
||||
|
||||
// then
|
||||
List<Image> images = dockerClient.listImagesCmd().exec();
|
||||
assertThat(images.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRemovingImage_thenImageListDecrease() {
|
||||
|
||||
// when
|
||||
List<Image> images = dockerClient.listImagesCmd().exec();
|
||||
Image image = images.get(0);
|
||||
dockerClient.removeImageCmd(image.getId()).exec();
|
||||
|
||||
// then
|
||||
List<Image> imagesNow = dockerClient.listImagesCmd().exec();
|
||||
assertThat(imagesNow.size(), is(lessThan(images.size())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSearchingImage_thenMustReturn25Items() {
|
||||
|
||||
// when
|
||||
List<SearchItem> items = dockerClient.searchImagesCmd("Java").exec();
|
||||
|
||||
// then
|
||||
assertThat(items.size(), is(25));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.baeldung.dockerapi;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.CreateNetworkResponse;
|
||||
import com.github.dockerjava.api.model.Network;
|
||||
import com.github.dockerjava.api.model.Network.Ipam;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
|
||||
public class NetworkLiveTest {
|
||||
|
||||
private static DockerClient dockerClient;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
dockerClient = DockerClientBuilder.getInstance().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("temporarily")
|
||||
public void whenListingNetworks_thenSizeMustBeGreaterThanZero() {
|
||||
|
||||
// when
|
||||
List<Network> networks = dockerClient.listNetworksCmd().exec();
|
||||
|
||||
// then
|
||||
assertThat(networks.size(), is(greaterThan(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingNetwork_thenRetrieveResponse() {
|
||||
|
||||
// when
|
||||
CreateNetworkResponse networkResponse = dockerClient.createNetworkCmd().withName("baeldungDefault").withDriver("bridge").exec();
|
||||
|
||||
// then
|
||||
assertThat(networkResponse, is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingAdvanceNetwork_thenRetrieveResponse() {
|
||||
|
||||
// when
|
||||
CreateNetworkResponse networkResponse = dockerClient.createNetworkCmd().withName("baeldungAdvanced").withIpam(new Ipam().withConfig(new Ipam.Config().withSubnet("172.36.0.0/16").withIpRange("172.36.5.0/24"))).withDriver("bridge").exec();
|
||||
|
||||
// then
|
||||
assertThat(networkResponse, is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenInspectingNetwork_thenSizeMustBeGreaterThanZero() {
|
||||
|
||||
// when
|
||||
String networkName = "bridge";
|
||||
Network network = dockerClient.inspectNetworkCmd().withNetworkId(networkName).exec();
|
||||
|
||||
// then
|
||||
MatcherAssert.assertThat(network.getName(), is(networkName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingNetwork_thenRemove() throws InterruptedException {
|
||||
|
||||
// when
|
||||
CreateNetworkResponse networkResponse = dockerClient.createNetworkCmd().withName("baeldungDefault").withDriver("bridge").exec();
|
||||
|
||||
// then
|
||||
Thread.sleep(4000);
|
||||
dockerClient.removeNetworkCmd(networkResponse.getId()).exec();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.baeldung.dockerapi;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.CreateVolumeResponse;
|
||||
import com.github.dockerjava.api.command.InspectVolumeResponse;
|
||||
import com.github.dockerjava.api.command.ListVolumesResponse;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
|
||||
public class VolumeLiveTest {
|
||||
|
||||
private static DockerClient dockerClient;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
dockerClient = DockerClientBuilder.getInstance().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingVolumes_thenSizeMustBeGreaterThanZero() {
|
||||
|
||||
// when
|
||||
ListVolumesResponse volumesResponse = dockerClient.listVolumesCmd().exec();
|
||||
|
||||
// then
|
||||
List<InspectVolumeResponse> volumes = volumesResponse.getVolumes();
|
||||
assertThat(volumes.size(), is(greaterThan(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenVolumes_whenInspectingVolume_thenReturnNonNullResponse() {
|
||||
|
||||
// given
|
||||
ListVolumesResponse volumesResponse = dockerClient.listVolumesCmd().exec();
|
||||
List<InspectVolumeResponse> volumes = volumesResponse.getVolumes();
|
||||
InspectVolumeResponse volume = volumes.get(0);
|
||||
|
||||
// when
|
||||
InspectVolumeResponse volumeResponse = dockerClient.inspectVolumeCmd(volume.getName()).exec();
|
||||
|
||||
// then
|
||||
assertThat(volumeResponse, is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingUnnamedVolume_thenGetVolumeId() {
|
||||
|
||||
// when
|
||||
CreateVolumeResponse unnamedVolume = dockerClient.createVolumeCmd().exec();
|
||||
|
||||
// then
|
||||
MatcherAssert.assertThat(unnamedVolume.getName(), is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingNamedVolume_thenGetVolumeId() {
|
||||
|
||||
// when
|
||||
CreateVolumeResponse namedVolume = dockerClient.createVolumeCmd().withName("myNamedVolume").exec();
|
||||
|
||||
// then
|
||||
MatcherAssert.assertThat(namedVolume.getName(), is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGettingNamedVolume_thenRemove() throws InterruptedException {
|
||||
|
||||
// when
|
||||
CreateVolumeResponse namedVolume = dockerClient.createVolumeCmd().withName("anotherNamedVolume").exec();
|
||||
|
||||
// then
|
||||
Thread.sleep(4000);
|
||||
dockerClient.removeVolumeCmd(namedVolume.getName()).exec();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.baeldung.fugue;
|
||||
|
||||
import static io.atlassian.fugue.Unit.Unit;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import io.atlassian.fugue.*;
|
||||
|
||||
public class FugueUnitTest {
|
||||
|
||||
@Test
|
||||
public void whenSome_thenDefined() {
|
||||
Option<String> some = Option.some("value");
|
||||
|
||||
assertTrue(some.isDefined());
|
||||
assertEquals("value", some.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNone_thenNotDefined() {
|
||||
Option<Object> none = Option.none();
|
||||
|
||||
assertFalse(none.isDefined());
|
||||
assertEquals(1, none.getOrElse(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSomeNull_thenException() {
|
||||
try {
|
||||
Option.some(null);
|
||||
|
||||
fail("some(null) should throw");
|
||||
} catch (NullPointerException e) {
|
||||
//Expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNullOption_thenSome() {
|
||||
Option<String> some = Option.some("value") .map(String::toUpperCase);
|
||||
|
||||
assertEquals("VALUE", some.get());
|
||||
|
||||
some = some.map(x -> null);
|
||||
|
||||
assertNull(some.get());
|
||||
some.forEach(Assert::assertNull);
|
||||
for(Object value : some) {
|
||||
assertNull(value);
|
||||
}
|
||||
assertEquals(1, some.toStream().count());
|
||||
|
||||
Option<Object> none = Option.some("value").flatMap(x -> Option.none());
|
||||
|
||||
assertFalse(none.isDefined());
|
||||
|
||||
none = Option.some("value").flatMap(Options.nullSafe(x -> null));
|
||||
|
||||
assertFalse(none.isDefined());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNone_thenEmptyOptional() {
|
||||
Optional<Object> optional = Option.none().toOptional();
|
||||
|
||||
assertFalse(optional.isPresent());
|
||||
assertTrue(Option.fromOptional(optional).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenOption_thenIterable() {
|
||||
Option<String> some = Option.some("value");
|
||||
Iterable<String> strings = Iterables.concat(some, Arrays.asList("a", "b", "c"));
|
||||
List<String> stringList = new ArrayList<>();
|
||||
Iterables.addAll(stringList, strings);
|
||||
|
||||
assertEquals(4, stringList.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenOption_thenStream() {
|
||||
assertEquals(0, Option.none().toStream().count());
|
||||
assertEquals(1, Option.some("value").toStream().count());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenLift_thenPartialFunction() {
|
||||
Function<Integer, Integer> f = (Integer x) -> x > 0 ? x + 1 : null;
|
||||
Function<Option<Integer>, Option<Integer>> lifted = Options.lift(f);
|
||||
|
||||
assertEquals(2, (long) lifted.apply(Option.some(1)).get());
|
||||
assertTrue(lifted.apply(Option.none()).isEmpty());
|
||||
assertEquals(null, lifted.apply(Option.some(0)).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
public void whenLeft_thenEither() {
|
||||
Either<Integer, String> right = Either.right("value");
|
||||
Either<Integer, String> left = Either.left(-1);
|
||||
if(right.isLeft()) {
|
||||
fail();
|
||||
}
|
||||
if(left.isRight()) {
|
||||
fail();
|
||||
}
|
||||
|
||||
String s = right.map(String::toUpperCase).getOrNull();
|
||||
|
||||
assertEquals("VALUE", s);
|
||||
|
||||
Either<String, String> either = right.left().map(x -> decodeSQLErrorCode(x));
|
||||
|
||||
assertTrue(either.isRight());
|
||||
assertEquals("value", either.right().get());
|
||||
|
||||
either.right().forEach(x -> assertEquals("value", x));
|
||||
}
|
||||
|
||||
private static String decodeSQLErrorCode(Integer x) {
|
||||
return "error";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryIsFailure_thenIsFailureReturnsTrue() {
|
||||
assertTrue(Try.failure(new Exception("Fail!")).isFailure());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryIsSuccess_thenIsSuccessReturnsTrue() {
|
||||
assertTrue(Try.successful("OK").isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFunctionReturning_whenCheckedOf_thenSuccess() {
|
||||
assertTrue(Checked.of(() -> "ok").isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFunctionThrowing_whenCheckedOf_thenFailure() {
|
||||
assertTrue(Checked.of(() -> { throw new Exception("ko"); }).isFailure());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFunctionThrowing_whenCheckedLift_thenFailure() {
|
||||
Checked.Function<String, Object, Exception> throwException = (String x) -> {
|
||||
throw new Exception(x);
|
||||
};
|
||||
|
||||
assertTrue(Checked.lift(throwException).apply("ko").isFailure());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRecover_thenSuccessfulTry() {
|
||||
Try<Object> recover = Try.
|
||||
failure(new Exception("boo!")).
|
||||
recover((Exception e) -> e.getMessage() + " recovered.");
|
||||
|
||||
assertTrue(recover.isSuccess());
|
||||
assertEquals("boo! recovered.", recover.getOrElse(() -> null));
|
||||
|
||||
recover = Try.
|
||||
failure(new Exception("boo!")).
|
||||
recoverWith((Exception e) -> Try.successful("recovered again!"));
|
||||
|
||||
assertTrue(recover.isSuccess());
|
||||
assertEquals("recovered again!", recover.getOrElse(() -> null));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void whenFailure_thenMapNotCalled() {
|
||||
Try<Object> recover = Try.failure(new Exception("boo!")).map(x -> {
|
||||
fail("Oh, no!");
|
||||
return null;
|
||||
}).recover(Function.identity());
|
||||
Exception exception = (Exception) recover.toOption().get();
|
||||
|
||||
assertTrue(recover.isSuccess());
|
||||
assertEquals("boo!", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenException_thenTryThrows() {
|
||||
Try<Object> checked = Checked.of(() -> {
|
||||
throw new Exception("Aaargh!");
|
||||
});
|
||||
Either<Exception, Object> either = checked.toEither();
|
||||
|
||||
assertTrue(checked.isFailure());
|
||||
assertTrue(either.isLeft());
|
||||
assertEquals(42, checked.getOrElse(() -> 42));
|
||||
|
||||
try {
|
||||
checked.getOrElse(() -> {
|
||||
throw new NoSuchElementException("fail");
|
||||
});
|
||||
|
||||
fail("Was expecting exception");
|
||||
} catch (Exception e) {
|
||||
assertEquals("fail", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRecoverThrows_thenFailure() {
|
||||
Try<Object> failure = Try.failure(new Exception("boo!")).recover(x -> {
|
||||
throw new RuntimeException(x);
|
||||
});
|
||||
|
||||
assertTrue(failure.isFailure());
|
||||
}
|
||||
|
||||
Unit doSomething() {
|
||||
System.out.println("Hello! Side effect");
|
||||
return Unit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenPair_thenLeftAndRight() {
|
||||
Pair<Integer, String> pair = Pair.pair(1, "a");
|
||||
|
||||
assertEquals(1, (int) pair.left());
|
||||
assertEquals("a", pair.right());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.baeldung.jnats;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import io.nats.client.Message;
|
||||
import io.nats.client.SyncSubscription;
|
||||
|
||||
public class NatsClientLiveTest {
|
||||
|
||||
@Test
|
||||
public void givenMessageExchange_MessagesReceived() throws Exception {
|
||||
|
||||
NatsClient client = connectClient();
|
||||
|
||||
SyncSubscription fooSubscription = client.subscribeSync("foo.bar");
|
||||
SyncSubscription barSubscription = client.subscribeSync("bar.foo");
|
||||
client.publishMessage("foo.bar", "bar.foo", "hello there");
|
||||
|
||||
Message message = fooSubscription.nextMessage(200);
|
||||
assertNotNull("No message!", message);
|
||||
assertEquals("hello there", new String(message.getData()));
|
||||
|
||||
client.publishMessage(message.getReplyTo(), message.getSubject(), "hello back");
|
||||
|
||||
message = barSubscription.nextMessage(200);
|
||||
assertNotNull("No message!", message);
|
||||
assertEquals("hello back", new String(message.getData()));
|
||||
}
|
||||
|
||||
|
||||
private NatsClient connectClient() {
|
||||
return new NatsClient();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenWildCardSubscription_andMatchTopic_MessageReceived() throws Exception {
|
||||
|
||||
NatsClient client = connectClient();
|
||||
|
||||
SyncSubscription fooSubscription = client.subscribeSync("foo.*");
|
||||
|
||||
client.publishMessage("foo.bar", "bar.foo", "hello there");
|
||||
|
||||
Message message = fooSubscription.nextMessage(200);
|
||||
assertNotNull("No message!", message);
|
||||
assertEquals("hello there", new String(message.getData()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenWildCardSubscription_andNotMatchTopic_NoMessageReceived() throws Exception {
|
||||
|
||||
NatsClient client = connectClient();
|
||||
|
||||
SyncSubscription fooSubscription = client.subscribeSync("foo.*");
|
||||
|
||||
client.publishMessage("foo.bar.plop", "bar.foo", "hello there");
|
||||
|
||||
Message message = fooSubscription.nextMessage(200);
|
||||
assertNull("Got message!", message);
|
||||
|
||||
|
||||
SyncSubscription barSubscription = client.subscribeSync("foo.>");
|
||||
|
||||
client.publishMessage("foo.bar.plop", "bar.foo", "hello there");
|
||||
|
||||
message = barSubscription.nextMessage(200);
|
||||
assertNotNull("No message!", message);
|
||||
assertEquals("hello there", new String(message.getData()));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void givenRequest_ReplyReceived() {
|
||||
|
||||
NatsClient client = connectClient();
|
||||
client.installReply("salary.requests", "denied!");
|
||||
|
||||
Message reply = client.makeRequest("salary.requests", "I need a raise.");
|
||||
assertNotNull("No message!", reply);
|
||||
assertEquals("denied!", new String(reply.getData()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenQueueMessage_OnlyOneReceived() throws Exception {
|
||||
|
||||
NatsClient client = connectClient();
|
||||
|
||||
SyncSubscription queue1 = client.joinQueueGroup("foo.bar.requests", "queue1");
|
||||
SyncSubscription queue2 = client.joinQueueGroup("foo.bar.requests", "queue1");
|
||||
|
||||
client.publishMessage("foo.bar.requests", "queuerequestor", "foobar");
|
||||
|
||||
List<Message> messages = new ArrayList<>();
|
||||
|
||||
Message message = queue1.nextMessage(200);
|
||||
|
||||
if (message != null) messages.add(message);
|
||||
message = queue2.nextMessage(200);
|
||||
|
||||
if (message != null) messages.add(message);
|
||||
assertEquals(1, messages.size());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.baeldung.jool;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static org.jooq.lambda.tuple.Tuple.tuple;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jooq.lambda.Seq;
|
||||
import org.jooq.lambda.Unchecked;
|
||||
import org.jooq.lambda.function.Function1;
|
||||
import org.jooq.lambda.function.Function2;
|
||||
import org.jooq.lambda.tuple.Tuple2;
|
||||
import org.jooq.lambda.tuple.Tuple3;
|
||||
import org.jooq.lambda.tuple.Tuple4;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JOOLUnitTest {
|
||||
@Test
|
||||
public void givenSeq_whenCheckContains_shouldReturnTrue() {
|
||||
List<Integer> concat = Seq.of(1, 2, 3).concat(Seq.of(4, 5, 6)).toList();
|
||||
|
||||
assertEquals(concat, Arrays.asList(1, 2, 3, 4, 5, 6));
|
||||
|
||||
assertTrue(Seq.of(1, 2, 3, 4).contains(2));
|
||||
|
||||
assertTrue(Seq.of(1, 2, 3, 4).containsAll(2, 3));
|
||||
|
||||
assertTrue(Seq.of(1, 2, 3, 4).containsAny(2, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenStreams_whenJoin_shouldHaveElementsFromTwoStreams() {
|
||||
// given
|
||||
Stream<Integer> left = Stream.of(1, 2, 4);
|
||||
Stream<Integer> right = Stream.of(1, 2, 3);
|
||||
|
||||
// when
|
||||
List<Integer> rightCollected = right.collect(Collectors.toList());
|
||||
List<Integer> collect = left.filter(rightCollected::contains).collect(Collectors.toList());
|
||||
|
||||
// then
|
||||
assertEquals(collect, Arrays.asList(1, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSeq_whenJoin_shouldHaveElementsFromBothSeq() {
|
||||
assertEquals(Seq.of(1, 2, 4).innerJoin(Seq.of(1, 2, 3), Objects::equals).toList(), Arrays.asList(tuple(1, 1), tuple(2, 2)));
|
||||
|
||||
assertEquals(Seq.of(1, 2, 4).leftOuterJoin(Seq.of(1, 2, 3), Objects::equals).toList(), Arrays.asList(tuple(1, 1), tuple(2, 2), tuple(4, null)));
|
||||
|
||||
assertEquals(Seq.of(1, 2, 4).rightOuterJoin(Seq.of(1, 2, 3), Objects::equals).toList(), Arrays.asList(tuple(1, 1), tuple(2, 2), tuple(null, 3)));
|
||||
|
||||
assertEquals(Seq.of(1, 2).crossJoin(Seq.of("A", "B")).toList(), Arrays.asList(tuple(1, "A"), tuple(1, "B"), tuple(2, "A"), tuple(2, "B")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSeq_whenManipulateSeq_seqShouldHaveNewElementsInIt() {
|
||||
assertEquals(Seq.of(1, 2, 3).cycle().limit(9).toList(), Arrays.asList(1, 2, 3, 1, 2, 3, 1, 2, 3));
|
||||
|
||||
assertEquals(Seq.of(1, 2, 3).duplicate().map((first, second) -> tuple(first.toList(), second.toList())), tuple(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3)));
|
||||
|
||||
assertEquals(Seq.of(1, 2, 3, 4).intersperse(0).toList(), Arrays.asList(1, 0, 2, 0, 3, 0, 4));
|
||||
|
||||
assertEquals(Seq.of(1, 2, 3, 4, 5).shuffle().toList().size(), 5);
|
||||
|
||||
assertEquals(Seq.of(1, 2, 3, 4).partition(i -> i > 2).map((first, second) -> tuple(first.toList(), second.toList())), tuple(Arrays.asList(3, 4), Arrays.asList(1, 2))
|
||||
|
||||
);
|
||||
|
||||
assertEquals(Seq.of(1, 2, 3, 4).reverse().toList(), Arrays.asList(4, 3, 2, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSeq_whenGroupByAndFold_shouldReturnProperSeq() {
|
||||
|
||||
Map<Integer, List<Integer>> expectedAfterGroupBy = new HashMap<>();
|
||||
expectedAfterGroupBy.put(1, Arrays.asList(1, 3));
|
||||
expectedAfterGroupBy.put(0, Arrays.asList(2, 4));
|
||||
|
||||
assertEquals(Seq.of(1, 2, 3, 4).groupBy(i -> i % 2), expectedAfterGroupBy);
|
||||
|
||||
assertEquals(Seq.of("a", "b", "c").foldLeft("!", (u, t) -> u + t), "!abc");
|
||||
|
||||
assertEquals(Seq.of("a", "b", "c").foldRight("!", (t, u) -> t + u), "abc!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSeq_whenUsingSeqWhile_shouldBehaveAsWhileLoop() {
|
||||
|
||||
assertEquals(Seq.of(1, 2, 3, 4, 5).skipWhile(i -> i < 3).toList(), Arrays.asList(3, 4, 5));
|
||||
|
||||
assertEquals(Seq.of(1, 2, 3, 4, 5).skipUntil(i -> i == 3).toList(), Arrays.asList(3, 4, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSeq_whenZip_shouldHaveZippedSeq() {
|
||||
|
||||
assertEquals(Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c")).toList(), Arrays.asList(tuple(1, "a"), tuple(2, "b"), tuple(3, "c")));
|
||||
|
||||
assertEquals(Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c"), (x, y) -> x + ":" + y).toList(), Arrays.asList("1:a", "2:b", "3:c"));
|
||||
|
||||
assertEquals(Seq.of("a", "b", "c").zipWithIndex().toList(), Arrays.asList(tuple("a", 0L), tuple("b", 1L), tuple("c", 2L)));
|
||||
}
|
||||
|
||||
public Integer methodThatThrowsChecked(String arg) throws Exception {
|
||||
return arg.length();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenOperationThatThrowsCheckedException_whenExecuteAndNeedToWrapCheckedIntoUnchecked_shouldPass() {
|
||||
// when
|
||||
List<Integer> collect = Stream.of("a", "b", "c").map(elem -> {
|
||||
try {
|
||||
return methodThatThrowsChecked(elem);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// then
|
||||
assertEquals(collect, Arrays.asList(1, 1, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenOperationThatThrowsCheckedException_whenExecuteUsingUncheckedFuction_shouldPass() {
|
||||
// when
|
||||
List<Integer> collect = Stream.of("a", "b", "c").map(Unchecked.function(this::methodThatThrowsChecked)).collect(Collectors.toList());
|
||||
|
||||
// then
|
||||
assertEquals(collect, Arrays.asList(1, 1, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFunction_whenAppliedPartially_shouldAddNumberToPartialArgument() {
|
||||
// given
|
||||
Function2<Integer, Integer, Integer> addTwoNumbers = (v1, v2) -> v1 + v2;
|
||||
addTwoNumbers.toBiFunction();
|
||||
Function1<Integer, Integer> addToTwo = addTwoNumbers.applyPartially(2);
|
||||
|
||||
// when
|
||||
Integer result = addToTwo.apply(5);
|
||||
|
||||
// then
|
||||
assertEquals(result, (Integer) 7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSeqOfTuples_whenTransformToLowerNumberOfTuples_shouldHaveProperResult() {
|
||||
// given
|
||||
Seq<Tuple3<String, String, Integer>> personDetails = Seq.of(tuple("michael", "similar", 49), tuple("jodie", "variable", 43));
|
||||
Tuple2<String, String> tuple = tuple("winter", "summer");
|
||||
|
||||
// when
|
||||
List<Tuple4<String, String, String, String>> result = personDetails.map(t -> t.limit2().concat(tuple)).toList();
|
||||
|
||||
// then
|
||||
assertEquals(result, Arrays.asList(tuple("michael", "similar", "winter", "summer"), tuple("jodie", "variable", "winter", "summer")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
FROM alpine:3.6
|
||||
|
||||
RUN apk --update add git openssh && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
rm /var/cache/apk/*
|
||||
|
||||
ENTRYPOINT ["git"]
|
||||
CMD ["--help"]
|
||||
Reference in New Issue
Block a user