Merge remote-tracking branch 'upstream/master'
This commit is contained in:
+36
-34
@@ -2,46 +2,48 @@ package com.baeldung.spliteratorAPI;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Spliterator;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class RelatedAuthorSpliterator implements Spliterator<Author> {
|
||||
private final List<Author> list;
|
||||
private int current = 0;
|
||||
private final List<Author> list;
|
||||
AtomicInteger current = new AtomicInteger();
|
||||
|
||||
public RelatedAuthorSpliterator(List<Author> list) {
|
||||
this.list = list;
|
||||
}
|
||||
public RelatedAuthorSpliterator(List<Author> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super Author> action) {
|
||||
action.accept(list.get(current++));
|
||||
return current < list.size();
|
||||
}
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super Author> action) {
|
||||
|
||||
@Override
|
||||
public Spliterator<Author> trySplit() {
|
||||
int currentSize = list.size() - current;
|
||||
if (currentSize < 10) {
|
||||
return null;
|
||||
}
|
||||
for (int splitPos = currentSize / 2 + current; splitPos < list.size(); splitPos++) {
|
||||
if (list.get(splitPos)
|
||||
.getRelatedArticleId() == 0) {
|
||||
Spliterator<Author> spliterator = new RelatedAuthorSpliterator(list.subList(current, splitPos));
|
||||
current = splitPos;
|
||||
return spliterator;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
action.accept(list.get(current.getAndIncrement()));
|
||||
return current.get() < list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimateSize() {
|
||||
return list.size() - current;
|
||||
}
|
||||
@Override
|
||||
public Spliterator<Author> trySplit() {
|
||||
int currentSize = list.size() - current.get();
|
||||
if (currentSize < 10) {
|
||||
return null;
|
||||
}
|
||||
for (int splitPos = currentSize / 2 + current.intValue(); splitPos < list.size(); splitPos++) {
|
||||
if (list.get(splitPos).getRelatedArticleId() == 0) {
|
||||
Spliterator<Author> spliterator = new RelatedAuthorSpliterator(list.subList(current.get(), splitPos));
|
||||
current.set(splitPos);
|
||||
return spliterator;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimateSize() {
|
||||
return list.size() - current.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int characteristics() {
|
||||
return CONCURRENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int characteristics() {
|
||||
return SIZED + CONCURRENT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.baeldung.concurrent.waitandnotify;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class Data {
|
||||
private String packet;
|
||||
|
||||
@@ -11,7 +13,9 @@ public class Data {
|
||||
while (transfer) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {}
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("Thread Interrupted");
|
||||
}
|
||||
}
|
||||
transfer = true;
|
||||
|
||||
@@ -23,7 +27,9 @@ public class Data {
|
||||
while (!transfer) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {}
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("Thread Interrupted");
|
||||
}
|
||||
}
|
||||
transfer = false;
|
||||
|
||||
|
||||
+3
-1
@@ -19,7 +19,9 @@ public class Receiver implements Runnable {
|
||||
//Thread.sleep() to mimic heavy server-side processing
|
||||
try {
|
||||
Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000));
|
||||
} catch (InterruptedException e) {}
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("Thread Interrupted");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-6
@@ -11,11 +11,11 @@ public class Sender implements Runnable {
|
||||
|
||||
public void run() {
|
||||
String packets[] = {
|
||||
"First packet",
|
||||
"Second packet",
|
||||
"Third packet",
|
||||
"Fourth packet",
|
||||
"End"
|
||||
"First packet",
|
||||
"Second packet",
|
||||
"Third packet",
|
||||
"Fourth packet",
|
||||
"End"
|
||||
};
|
||||
|
||||
for (String packet : packets) {
|
||||
@@ -24,7 +24,9 @@ public class Sender implements Runnable {
|
||||
//Thread.sleep() to mimic heavy server-side processing
|
||||
try {
|
||||
Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000));
|
||||
} catch (InterruptedException e) {}
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("Thread Interrupted");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
-39
@@ -13,38 +13,38 @@ import org.junit.Test;
|
||||
|
||||
public class NetworkIntegrationTest {
|
||||
|
||||
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
|
||||
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
|
||||
private String expected;
|
||||
|
||||
@Before
|
||||
public void setUpStreams() {
|
||||
System.setOut(new PrintStream(outContent));
|
||||
System.setErr(new PrintStream(errContent));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpExpectedOutput() {
|
||||
StringWriter expectedStringWriter = new StringWriter();
|
||||
|
||||
PrintWriter printWriter = new PrintWriter(expectedStringWriter);
|
||||
printWriter.println("First packet");
|
||||
printWriter.println("Second packet");
|
||||
printWriter.println("Third packet");
|
||||
printWriter.println("Fourth packet");
|
||||
printWriter.close();
|
||||
|
||||
expected = expectedStringWriter.toString();
|
||||
}
|
||||
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
|
||||
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
|
||||
private String expected;
|
||||
|
||||
@Before
|
||||
public void setUpStreams() {
|
||||
System.setOut(new PrintStream(outContent));
|
||||
System.setErr(new PrintStream(errContent));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpExpectedOutput() {
|
||||
StringWriter expectedStringWriter = new StringWriter();
|
||||
|
||||
PrintWriter printWriter = new PrintWriter(expectedStringWriter);
|
||||
printWriter.println("First packet");
|
||||
printWriter.println("Second packet");
|
||||
printWriter.println("Third packet");
|
||||
printWriter.println("Fourth packet");
|
||||
printWriter.close();
|
||||
|
||||
expected = expectedStringWriter.toString();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpStreams() {
|
||||
System.setOut(null);
|
||||
System.setErr(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSenderAndReceiver_whenSendingPackets_thenNetworkSynchronized() {
|
||||
@After
|
||||
public void cleanUpStreams() {
|
||||
System.setOut(null);
|
||||
System.setErr(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSenderAndReceiver_whenSendingPackets_thenNetworkSynchronized() {
|
||||
Data data = new Data();
|
||||
Thread sender = new Thread(new Sender(data));
|
||||
Thread receiver = new Thread(new Receiver(data));
|
||||
@@ -54,12 +54,12 @@ public class NetworkIntegrationTest {
|
||||
|
||||
//wait for sender and receiver to finish before we test against expected
|
||||
try {
|
||||
sender.join();
|
||||
receiver.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
assertEquals(expected, outContent.toString());
|
||||
}
|
||||
sender.join();
|
||||
receiver.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
assertEquals(expected, outContent.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,3 +126,4 @@
|
||||
- [A Guide to Inner Interfaces in Java](http://www.baeldung.com/java-inner-interfaces)
|
||||
- [Polymorphism in Java](http://www.baeldung.com/java-polymorphism)
|
||||
- [Recursion In Java](http://www.baeldung.com/java-recursion)
|
||||
- [A Guide to the finalize Method in Java](http://www.baeldung.com/java-finalize)
|
||||
|
||||
@@ -49,6 +49,11 @@
|
||||
<artifactId>kotlin-stdlib-jre8</artifactId>
|
||||
<version>${kotlin-stdlib.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>khttp</groupId>
|
||||
<artifactId>khttp</artifactId>
|
||||
<version>0.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test-junit</artifactId>
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.baeldung.kotlin.khttp
|
||||
|
||||
import khttp.structures.files.FileLike
|
||||
import org.json.JSONObject
|
||||
import org.junit.Test
|
||||
import java.beans.ExceptionListener
|
||||
import java.beans.XMLEncoder
|
||||
import java.io.*
|
||||
import java.lang.Exception
|
||||
import java.net.ConnectException
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.fail
|
||||
|
||||
class KhttpTest {
|
||||
|
||||
@Test
|
||||
fun whenHttpGetRequestIsMade_thenArgsAreReturned() {
|
||||
val response = khttp.get(
|
||||
url = "http://httpbin.org/get",
|
||||
params = mapOf("p1" to "1", "p2" to "2"))
|
||||
val args = response.jsonObject.getJSONObject("args")
|
||||
|
||||
assertEquals("1", args["p1"])
|
||||
assertEquals("2", args["p2"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAlternateHttpGetRequestIsMade_thenArgsAreReturned() {
|
||||
val response = khttp.request(
|
||||
method = "GET",
|
||||
url = "http://httpbin.org/get",
|
||||
params = mapOf("p1" to "1", "p2" to "2"))
|
||||
val args = response.jsonObject.getJSONObject("args")
|
||||
|
||||
assertEquals("1", args["p1"])
|
||||
assertEquals("2", args["p2"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenHeadersAreSet_thenHeadersAreSent() {
|
||||
val response = khttp.get(
|
||||
url = "http://httpbin.org/get",
|
||||
headers = mapOf("header1" to "1", "header2" to "2"))
|
||||
val headers = response.jsonObject.getJSONObject("headers")
|
||||
|
||||
assertEquals("1", headers["Header1"])
|
||||
assertEquals("2", headers["Header2"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenHttpPostRequestIsMadeWithJson_thenBodyIsReturned() {
|
||||
val response = khttp.post(
|
||||
url = "http://httpbin.org/post",
|
||||
params = mapOf("p1" to "1", "p2" to "2"),
|
||||
json = mapOf("pr1" to "1", "pr2" to "2"))
|
||||
|
||||
val args = response.jsonObject.getJSONObject("args")
|
||||
|
||||
assertEquals("1", args["p1"])
|
||||
assertEquals("2", args["p2"])
|
||||
|
||||
val json = response.jsonObject.getJSONObject("json")
|
||||
|
||||
assertEquals("1", json["pr1"])
|
||||
assertEquals("2", json["pr2"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenHttpPostRequestIsMadeWithMapData_thenBodyIsReturned() {
|
||||
val response = khttp.post(
|
||||
url = "http://httpbin.org/post",
|
||||
params = mapOf("p1" to "1", "p2" to "2"),
|
||||
data = mapOf("pr1" to "1", "pr2" to "2"))
|
||||
|
||||
val args = response.jsonObject.getJSONObject("args")
|
||||
|
||||
assertEquals("1", args["p1"])
|
||||
assertEquals("2", args["p2"])
|
||||
|
||||
val form = response.jsonObject.getJSONObject("form")
|
||||
|
||||
assertEquals("1", form["pr1"])
|
||||
assertEquals("2", form["pr2"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenHttpPostRequestIsMadeWithFiles_thenBodyIsReturned() {
|
||||
val response = khttp.post(
|
||||
url = "http://httpbin.org/post",
|
||||
params = mapOf("p1" to "1", "p2" to "2"),
|
||||
files = listOf(
|
||||
FileLike("file1", "content1"),
|
||||
FileLike("file2", javaClass.getResource("KhttpTest.class").openStream().readBytes())))
|
||||
|
||||
val args = response.jsonObject.getJSONObject("args")
|
||||
|
||||
assertEquals("1", args["p1"])
|
||||
assertEquals("2", args["p2"])
|
||||
|
||||
val files = response.jsonObject.getJSONObject("files")
|
||||
|
||||
assertEquals("content1", files["file1"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenHttpPostRequestIsMadeWithInputStream_thenBodyIsReturned() {
|
||||
val response = khttp.post(
|
||||
url = "http://httpbin.org/post",
|
||||
params = mapOf("p1" to "1", "p2" to "2"),
|
||||
data = ByteArrayInputStream("content!".toByteArray()))
|
||||
|
||||
val args = response.jsonObject.getJSONObject("args")
|
||||
|
||||
assertEquals("1", args["p1"])
|
||||
assertEquals("2", args["p2"])
|
||||
|
||||
assertEquals("content!", response.jsonObject["data"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenHttpPostStreamingRequestIsMade_thenBodyIsReturnedInChunks() {
|
||||
val response = khttp.post(
|
||||
url = "http://httpbin.org/post",
|
||||
stream = true,
|
||||
json = mapOf("pr1" to "1", "pr2" to "2"))
|
||||
|
||||
val baos = ByteArrayOutputStream()
|
||||
response.contentIterator(chunkSize = 10).forEach { arr : ByteArray -> baos.write(arr) }
|
||||
val json = JSONObject(String(baos.toByteArray())).getJSONObject("json")
|
||||
|
||||
assertEquals("1", json["pr1"])
|
||||
assertEquals("2", json["pr2"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenHttpRequestFails_thenExceptionIsThrown() {
|
||||
try {
|
||||
khttp.get(url = "http://localhost/nothing/to/see/here")
|
||||
|
||||
fail("Should have thrown an exception")
|
||||
} catch (e : ConnectException) {
|
||||
//Ok
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenHttpNotFound_thenExceptionIsThrown() {
|
||||
val response = khttp.get(url = "http://httpbin.org/nothing/to/see/here")
|
||||
|
||||
assertEquals(404, response.statusCode)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.baeldung.guava.memoizer;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CostlySupplier {
|
||||
|
||||
public static BigInteger generateBigNumber() {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
return new BigInteger("12345");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.baeldung.guava.memoizer;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class Factorial {
|
||||
|
||||
private static LoadingCache<Integer, BigInteger> memo = CacheBuilder.newBuilder()
|
||||
.build(CacheLoader.from(Factorial::getFactorial));
|
||||
|
||||
public static BigInteger getFactorial(int n) {
|
||||
if (n == 0) {
|
||||
return BigInteger.ONE;
|
||||
} else {
|
||||
return BigInteger.valueOf(n).multiply(memo.getUnchecked(n - 1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.baeldung.guava.memoizer;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FibonacciSequence {
|
||||
|
||||
private static LoadingCache<Integer, BigInteger> memo = CacheBuilder.newBuilder()
|
||||
.maximumSize(100)
|
||||
.build(CacheLoader.from(FibonacciSequence::getFibonacciNumber));
|
||||
|
||||
public static BigInteger getFibonacciNumber(int n) {
|
||||
if (n == 0) {
|
||||
return BigInteger.ZERO;
|
||||
} else if (n == 1) {
|
||||
return BigInteger.ONE;
|
||||
} else {
|
||||
return memo.getUnchecked(n - 1).add(memo.getUnchecked(n - 2));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package org.baeldung.guava;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import org.baeldung.guava.memoizer.CostlySupplier;
|
||||
import org.baeldung.guava.memoizer.Factorial;
|
||||
import org.baeldung.guava.memoizer.FibonacciSequence;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.hamcrest.number.IsCloseTo.closeTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class GuavaMemoizerUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenInteger_whenGetFibonacciNumber_thenShouldCalculateFibonacciNumber() {
|
||||
// given
|
||||
int n = 95;
|
||||
|
||||
// when
|
||||
BigInteger fibonacciNumber = FibonacciSequence.getFibonacciNumber(n);
|
||||
|
||||
// then
|
||||
BigInteger expectedFibonacciNumber = new BigInteger("31940434634990099905");
|
||||
assertThat(fibonacciNumber, is(equalTo(expectedFibonacciNumber)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenInteger_whenGetFactorial_thenShouldCalculateFactorial() {
|
||||
// given
|
||||
int n = 95;
|
||||
|
||||
// when
|
||||
BigInteger factorial = new Factorial().getFactorial(n);
|
||||
|
||||
// then
|
||||
BigInteger expectedFactorial = new BigInteger("10329978488239059262599702099394727095397746340117372869212250571234293987594703124871765375385424468563282236864226607350415360000000000000000000000");
|
||||
assertThat(factorial, is(equalTo(expectedFactorial)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMemoizedSupplier_whenGet_thenSubsequentGetsAreFast() {
|
||||
Supplier<BigInteger> memoizedSupplier;
|
||||
memoizedSupplier = Suppliers.memoize(CostlySupplier::generateBigNumber);
|
||||
|
||||
Instant start = Instant.now();
|
||||
BigInteger bigNumber = memoizedSupplier.get();
|
||||
Long durationInMs = Duration.between(start, Instant.now()).toMillis();
|
||||
assertThat(bigNumber, is(equalTo(new BigInteger("12345"))));
|
||||
assertThat(durationInMs.doubleValue(), is(closeTo(2000D, 100D)));
|
||||
|
||||
start = Instant.now();
|
||||
bigNumber = memoizedSupplier.get().add(BigInteger.ONE);
|
||||
durationInMs = Duration.between(start, Instant.now()).toMillis();
|
||||
assertThat(bigNumber, is(equalTo(new BigInteger("12346"))));
|
||||
assertThat(durationInMs.doubleValue(), is(closeTo(0D, 100D)));
|
||||
|
||||
start = Instant.now();
|
||||
bigNumber = memoizedSupplier.get().add(BigInteger.TEN);
|
||||
durationInMs = Duration.between(start, Instant.now()).toMillis();
|
||||
assertThat(bigNumber, is(equalTo(new BigInteger("12355"))));
|
||||
assertThat(durationInMs.doubleValue(), is(closeTo(0D, 100D)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMemoizedSupplierWithExpiration_whenGet_thenSubsequentGetsBeforeExpiredAreFast() throws InterruptedException {
|
||||
Supplier<BigInteger> memoizedSupplier;
|
||||
memoizedSupplier = Suppliers.memoizeWithExpiration(CostlySupplier::generateBigNumber, 3, TimeUnit.SECONDS);
|
||||
|
||||
Instant start = Instant.now();
|
||||
BigInteger bigNumber = memoizedSupplier.get();
|
||||
Long durationInMs = Duration.between(start, Instant.now()).toMillis();
|
||||
assertThat(bigNumber, is(equalTo(new BigInteger("12345"))));
|
||||
assertThat(durationInMs.doubleValue(), is(closeTo(2000D, 100D)));
|
||||
|
||||
start = Instant.now();
|
||||
bigNumber = memoizedSupplier.get().add(BigInteger.ONE);
|
||||
durationInMs = Duration.between(start, Instant.now()).toMillis();
|
||||
assertThat(bigNumber, is(equalTo(new BigInteger("12346"))));
|
||||
assertThat(durationInMs.doubleValue(), is(closeTo(0D, 100D)));
|
||||
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
|
||||
start = Instant.now();
|
||||
bigNumber = memoizedSupplier.get().add(BigInteger.TEN);
|
||||
durationInMs = Duration.between(start, Instant.now()).toMillis();
|
||||
assertThat(bigNumber, is(equalTo(new BigInteger("12355"))));
|
||||
assertThat(durationInMs.doubleValue(), is(closeTo(2000D, 100D)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.baeldung.rmi</groupId>
|
||||
<artifactId>java-rmi</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.baeldung.rmi;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Message implements Serializable {
|
||||
|
||||
private String messageText;
|
||||
|
||||
private String contentType;
|
||||
|
||||
public Message() {
|
||||
}
|
||||
|
||||
public Message(String messageText, String contentType) {
|
||||
|
||||
this.messageText = messageText;
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public String getMessageText() {
|
||||
return messageText;
|
||||
}
|
||||
|
||||
public void setMessageText(String messageText) {
|
||||
this.messageText = messageText;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.baeldung.rmi;
|
||||
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
|
||||
public interface MessengerService extends Remote {
|
||||
|
||||
public String sendMessage(String clientMessage) throws RemoteException;
|
||||
|
||||
public Message sendMessage(Message clientMessage) throws RemoteException;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.baeldung.rmi;
|
||||
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
|
||||
public class MessengerServiceImpl implements MessengerService {
|
||||
|
||||
public String sendMessage(String clientMessage) {
|
||||
|
||||
String serverMessage = null;
|
||||
if (clientMessage.equals("Client Message")) {
|
||||
serverMessage = "Server Message";
|
||||
}
|
||||
|
||||
return serverMessage;
|
||||
}
|
||||
|
||||
public void createStubAndBind() throws RemoteException {
|
||||
|
||||
MessengerService stub = (MessengerService) UnicastRemoteObject.exportObject((MessengerService) this, 0);
|
||||
Registry registry = LocateRegistry.createRegistry(1099);
|
||||
registry.rebind("MessengerService", stub);
|
||||
}
|
||||
|
||||
public Message sendMessage(Message clientMessage) throws RemoteException {
|
||||
|
||||
Message serverMessage = null;
|
||||
if (clientMessage.getMessageText().equals("Client Message")) {
|
||||
serverMessage = new Message("Server Message", "text/plain");
|
||||
}
|
||||
|
||||
return serverMessage;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.baeldung.rmi;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.rmi.NotBoundException;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.registry.Registry;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JavaRMIIntegrationTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void whenRunServer_thenServerStarts() {
|
||||
|
||||
try {
|
||||
MessengerServiceImpl server = new MessengerServiceImpl();
|
||||
server.createStubAndBind();
|
||||
} catch (RemoteException e) {
|
||||
fail("Exception Occurred");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenClientSendsMessageToServer_thenServerSendsResponseMessage() {
|
||||
|
||||
try {
|
||||
Registry registry = LocateRegistry.getRegistry();
|
||||
MessengerService server = (MessengerService) registry.lookup("MessengerService");
|
||||
String responseMessage = server.sendMessage("Client Message");
|
||||
|
||||
String expectedMessage = "Server Message";
|
||||
assertEquals(responseMessage, expectedMessage);
|
||||
} catch (RemoteException e) {
|
||||
fail("Exception Occurred");
|
||||
} catch (NotBoundException nb) {
|
||||
fail("Exception Occurred");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+6
-2
@@ -194,6 +194,11 @@
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-webapp</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>rome</groupId>
|
||||
<artifactId>rome</artifactId>
|
||||
@@ -741,12 +746,11 @@
|
||||
<assertj.version>3.6.2</assertj.version>
|
||||
<jsonassert.version>1.5.0</jsonassert.version>
|
||||
<javers.version>3.1.0</javers.version>
|
||||
<jetty.version>9.4.3.v20170317</jetty.version>
|
||||
<httpclient.version>4.5.3</httpclient.version>
|
||||
<commons.io.version>2.5</commons.io.version>
|
||||
<commons.dbutils.version>1.6</commons.dbutils.version>
|
||||
<h2.version>1.4.196</h2.version>
|
||||
<jetty.version>9.4.2.v20170220</jetty.version>
|
||||
<jetty.version>9.4.8.v20171121</jetty.version>
|
||||
<httpclient.version>4.5.3</httpclient.version>
|
||||
<commons.io.version>2.5</commons.io.version>
|
||||
<flink.version>1.2.0</flink.version>
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.baeldung.jetty;
|
||||
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
/**
|
||||
* Simple factory for creating Jetty basic instances.
|
||||
*
|
||||
* @author Donato Rimenti
|
||||
*
|
||||
*/
|
||||
public class JettyServerFactory {
|
||||
|
||||
/**
|
||||
* Exposed context of the app.
|
||||
*/
|
||||
public final static String APP_PATH = "/myApp";
|
||||
|
||||
/**
|
||||
* The server port.
|
||||
*/
|
||||
public final static int SERVER_PORT = 13133;
|
||||
|
||||
/**
|
||||
* Private constructor to avoid instantiation.
|
||||
*/
|
||||
private JettyServerFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a simple server listening on port 80 with a timeout of 30 seconds
|
||||
* for connections and no handlers.
|
||||
*
|
||||
* @return a server
|
||||
*/
|
||||
public static Server createBaseServer() {
|
||||
Server server = new Server();
|
||||
|
||||
// Adds a connector for port 80 with a timeout of 30 seconds.
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(SERVER_PORT);
|
||||
connector.setHost("127.0.0.1");
|
||||
connector.setIdleTimeout(30000);
|
||||
server.addConnector(connector);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a server which delegates the request handling to a web
|
||||
* application.
|
||||
*
|
||||
* @return a server
|
||||
*/
|
||||
public static Server createWebAppServer() {
|
||||
// Adds an handler to a server and returns it.
|
||||
Server server = createBaseServer();
|
||||
String webAppFolderPath = JettyServerFactory.class.getClassLoader().getResource("jetty-embedded-demo-app.war")
|
||||
.getPath();
|
||||
Handler webAppHandler = new WebAppContext(webAppFolderPath, APP_PATH);
|
||||
server.setHandler(webAppHandler);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a server which delegates the request handling to both a logging
|
||||
* handler and to a web application, in this order.
|
||||
*
|
||||
* @return a server
|
||||
*/
|
||||
public static Server createMultiHandlerServer() {
|
||||
Server server = createBaseServer();
|
||||
|
||||
// Creates the handlers and adds them to the server.
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
|
||||
String webAppFolderPath = JettyServerFactory.class.getClassLoader().getResource("jetty-embedded-demo-app.war")
|
||||
.getPath();
|
||||
Handler customRequestHandler = new WebAppContext(webAppFolderPath, APP_PATH);
|
||||
handlers.addHandler(customRequestHandler);
|
||||
|
||||
Handler loggingRequestHandler = new LoggingRequestHandler();
|
||||
handlers.addHandler(loggingRequestHandler);
|
||||
|
||||
server.setHandler(handlers);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.baeldung.jetty;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Handler implementation which simply logs that a request has been received.
|
||||
*
|
||||
* @author Donato Rimenti
|
||||
*/
|
||||
public class LoggingRequestHandler implements Handler {
|
||||
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private final static Logger LOG = LoggerFactory.getLogger(LoggingRequestHandler.class);
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.util.component.LifeCycle#addLifeCycleListener(org.
|
||||
* eclipse.jetty.util.component.LifeCycle.Listener)
|
||||
*/
|
||||
@Override
|
||||
public void addLifeCycleListener(Listener arg0) {
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.util.component.LifeCycle#isFailed()
|
||||
*/
|
||||
@Override
|
||||
public boolean isFailed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.util.component.LifeCycle#isRunning()
|
||||
*/
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.util.component.LifeCycle#isStarted()
|
||||
*/
|
||||
@Override
|
||||
public boolean isStarted() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.util.component.LifeCycle#isStarting()
|
||||
*/
|
||||
@Override
|
||||
public boolean isStarting() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.util.component.LifeCycle#isStopped()
|
||||
*/
|
||||
@Override
|
||||
public boolean isStopped() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.util.component.LifeCycle#isStopping()
|
||||
*/
|
||||
@Override
|
||||
public boolean isStopping() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.eclipse.jetty.util.component.LifeCycle#removeLifeCycleListener(org.
|
||||
* eclipse.jetty.util.component.LifeCycle.Listener)
|
||||
*/
|
||||
@Override
|
||||
public void removeLifeCycleListener(Listener arg0) {
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.util.component.LifeCycle#start()
|
||||
*/
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.util.component.LifeCycle#stop()
|
||||
*/
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.server.Handler#destroy()
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.server.Handler#getServer()
|
||||
*/
|
||||
@Override
|
||||
public Server getServer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.server.Handler#handle(java.lang.String,
|
||||
* org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
|
||||
* javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
@Override
|
||||
public void handle(String arg0, Request arg1, HttpServletRequest arg2, HttpServletResponse arg3)
|
||||
throws IOException, ServletException {
|
||||
LOG.info("Received a new request");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.jetty.server.Handler#setServer(org.eclipse.jetty.server.
|
||||
* Server)
|
||||
*/
|
||||
@Override
|
||||
public void setServer(Server server) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.baeldung.jetty;
|
||||
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test for {@link JettyServerFactory}.
|
||||
*
|
||||
* @author Donato Rimenti
|
||||
*
|
||||
*/
|
||||
public class JettyServerFactoryUnitTest {
|
||||
|
||||
/**
|
||||
* Tests that when a base server is provided a request returns a status 404.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void givenBaseServer_whenHttpRequest_thenStatus404() throws Exception {
|
||||
Server server = JettyServerFactory.createBaseServer();
|
||||
server.start();
|
||||
|
||||
int statusCode = sendGetRequest();
|
||||
|
||||
Assert.assertEquals(404, statusCode);
|
||||
server.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that when a web app server is provided a request returns a status
|
||||
* 200.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void givenWebAppServer_whenHttpRequest_thenStatus200() throws Exception {
|
||||
Server server = JettyServerFactory.createWebAppServer();
|
||||
server.start();
|
||||
|
||||
int statusCode = sendGetRequest();
|
||||
|
||||
Assert.assertEquals(200, statusCode);
|
||||
server.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that when a multi handler server is provided a request returns a
|
||||
* status 200.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void givenMultiHandlerServerServer_whenHttpRequest_thenStatus200() throws Exception {
|
||||
Server server = JettyServerFactory.createMultiHandlerServer();
|
||||
server.start();
|
||||
|
||||
int statusCode = sendGetRequest();
|
||||
|
||||
Assert.assertEquals(200, statusCode);
|
||||
server.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a default HTTP GET request to the server and returns the response
|
||||
* status code.
|
||||
*
|
||||
* @return the status code of the response
|
||||
* @throws Exception
|
||||
*/
|
||||
private int sendGetRequest() throws Exception {
|
||||
HttpHost target = new HttpHost("localhost", JettyServerFactory.SERVER_PORT);
|
||||
HttpRequest request = new HttpGet(JettyServerFactory.APP_PATH);
|
||||
HttpClient client = HttpClientBuilder.create().build();
|
||||
HttpResponse response = client.execute(target, request);
|
||||
return response.getStatusLine().getStatusCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,9 +32,9 @@ public class PactConsumerDrivenContractUnitTest {
|
||||
headers.put("Content-Type", "application/json");
|
||||
|
||||
return builder
|
||||
.given("test GET ")
|
||||
.given("test GET")
|
||||
.uponReceiving("GET REQUEST")
|
||||
.path("/")
|
||||
.path("/pact")
|
||||
.method("GET")
|
||||
.willRespondWith()
|
||||
.status(200)
|
||||
@@ -45,11 +45,9 @@ public class PactConsumerDrivenContractUnitTest {
|
||||
.method("POST")
|
||||
.headers(headers)
|
||||
.body("{\"name\": \"Michael\"}")
|
||||
.path("/create")
|
||||
.path("/pact")
|
||||
.willRespondWith()
|
||||
.status(201)
|
||||
.headers(headers)
|
||||
.body("")
|
||||
.toPact();
|
||||
}
|
||||
|
||||
@@ -59,7 +57,7 @@ public class PactConsumerDrivenContractUnitTest {
|
||||
public void givenGet_whenSendRequest_shouldReturn200WithProperHeaderAndBody() {
|
||||
//when
|
||||
ResponseEntity<String> response
|
||||
= new RestTemplate().getForEntity(mockProvider.getUrl(), String.class);
|
||||
= new RestTemplate().getForEntity(mockProvider.getUrl() + "/pact", String.class);
|
||||
|
||||
//then
|
||||
assertThat(response.getStatusCode().value()).isEqualTo(200);
|
||||
@@ -73,7 +71,7 @@ public class PactConsumerDrivenContractUnitTest {
|
||||
|
||||
//when
|
||||
ResponseEntity<String> postResponse = new RestTemplate().exchange(
|
||||
mockProvider.getUrl() + "/create",
|
||||
mockProvider.getUrl() + "/pact",
|
||||
HttpMethod.POST,
|
||||
new HttpEntity<>(jsonBody, httpHeaders),
|
||||
String.class
|
||||
|
||||
Binary file not shown.
@@ -87,8 +87,9 @@
|
||||
<module>jackson</module>
|
||||
<!-- <module>persistence-modules/java-cassandra</module> -->
|
||||
<module>vavr</module>
|
||||
<module>java-lite</module>
|
||||
<module>java-vavr-stream</module>
|
||||
<module>java-lite</module>
|
||||
<module>java-rmi</module>
|
||||
<module>java-vavr-stream</module>
|
||||
<module>javax-servlets</module>
|
||||
<module>javaxval</module>
|
||||
<module>jaxb</module>
|
||||
|
||||
+9
-6
@@ -2,22 +2,25 @@ package com.baeldung.reactive.websocket;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.web.reactive.socket.WebSocketMessage;
|
||||
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
|
||||
import org.springframework.web.reactive.socket.client.WebSocketClient;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ReactiveJavaClientWebSocket {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
|
||||
WebSocketClient client = new ReactorNettyWebSocketClient();
|
||||
client.execute(URI.create("ws://localhost:8080/event-emitter"), session -> session.send(Mono.just(session.textMessage("event-me-from-spring-reactive-client")))
|
||||
client.execute(
|
||||
URI.create("ws://localhost:8080/event-emitter"),
|
||||
session -> session.send(
|
||||
Mono.just(session.textMessage("event-spring-reactive-client-websocket")))
|
||||
.thenMany(session.receive()
|
||||
.map(WebSocketMessage::getPayloadAsText)
|
||||
.log())
|
||||
.map(WebSocketMessage::getPayloadAsText)
|
||||
.log())
|
||||
.then())
|
||||
.block(Duration.ofSeconds(10L));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+19
-49
@@ -1,71 +1,41 @@
|
||||
package com.baeldung.reactive.websocket;
|
||||
|
||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
import org.springframework.web.reactive.socket.WebSocketMessage;
|
||||
|
||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
import static java.time.LocalDateTime.now;
|
||||
import static java.util.UUID.randomUUID;
|
||||
|
||||
@Component
|
||||
public class ReactiveWebSocketHandler implements WebSocketHandler {
|
||||
|
||||
private Flux<Event> eventFlux;
|
||||
private Flux<Event> intervalFlux;
|
||||
private static final ObjectMapper json = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* Here we prepare a Flux that will emit a message every second
|
||||
*/
|
||||
@PostConstruct
|
||||
private void init() throws InterruptedException {
|
||||
private Flux<String> eventFlux = Flux.generate(sink -> {
|
||||
Event event = new Event(randomUUID().toString(), now().toString());
|
||||
try {
|
||||
sink.next(json.writeValueAsString(event));
|
||||
} catch (JsonProcessingException e) {
|
||||
sink.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
eventFlux = Flux.generate(e -> {
|
||||
Event event = new Event(UUID.randomUUID()
|
||||
.toString(),
|
||||
LocalDateTime.now()
|
||||
.toString());
|
||||
e.next(event);
|
||||
});
|
||||
private Flux<String> intervalFlux = Flux.interval(Duration.ofMillis(1000L))
|
||||
.zipWith(eventFlux, (time, event) -> event);
|
||||
|
||||
intervalFlux = Flux.interval(Duration.ofMillis(1000L))
|
||||
.zipWith(eventFlux, (time, event) -> event);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* On each new client session, send the message flux to the client.
|
||||
* Spring subscribes to the flux and send every new flux event to the WebSocketSession object
|
||||
* @param session
|
||||
* @return Mono<Void>
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> handle(WebSocketSession webSocketSession) {
|
||||
ObjectMapper json = new ObjectMapper();
|
||||
return webSocketSession.send(intervalFlux.map(event -> {
|
||||
try {
|
||||
String jsonEvent = json.writeValueAsString(event);
|
||||
System.out.println(jsonEvent);
|
||||
return jsonEvent;
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
}
|
||||
})
|
||||
.map(webSocketSession::textMessage))
|
||||
|
||||
.and(webSocketSession.receive()
|
||||
.map(WebSocketMessage::getPayloadAsText)
|
||||
.log());
|
||||
return webSocketSession.send(intervalFlux
|
||||
.map(webSocketSession::textMessage))
|
||||
.and(webSocketSession.receive()
|
||||
.map(WebSocketMessage::getPayloadAsText).log());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.baeldung.loginextrafieldscustom;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
|
||||
|
||||
public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain";
|
||||
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
|
||||
throws AuthenticationException {
|
||||
|
||||
if (!request.getMethod().equals("POST")) {
|
||||
throw new AuthenticationServiceException("Authentication method not supported: "
|
||||
+ request.getMethod());
|
||||
}
|
||||
|
||||
CustomAuthenticationToken authRequest = getAuthRequest(request);
|
||||
setDetails(request, authRequest);
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
|
||||
private CustomAuthenticationToken getAuthRequest(HttpServletRequest request) {
|
||||
String username = obtainUsername(request);
|
||||
String password = obtainPassword(request);
|
||||
String domain = obtainDomain(request);
|
||||
|
||||
if (username == null) {
|
||||
username = "";
|
||||
}
|
||||
if (password == null) {
|
||||
password = "";
|
||||
}
|
||||
if (domain == null) {
|
||||
domain = "";
|
||||
}
|
||||
|
||||
return new CustomAuthenticationToken(username, password, domain);
|
||||
}
|
||||
|
||||
private String obtainDomain(HttpServletRequest request) {
|
||||
return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.baeldung.loginextrafieldscustom;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken {
|
||||
|
||||
private String domain;
|
||||
|
||||
public CustomAuthenticationToken(Object principal, Object credentials, String domain) {
|
||||
super(principal, credentials);
|
||||
this.domain = domain;
|
||||
super.setAuthenticated(false);
|
||||
}
|
||||
|
||||
public CustomAuthenticationToken(Object principal, Object credentials, String domain,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
super(principal, credentials, authorities);
|
||||
this.domain = domain;
|
||||
super.setAuthenticated(true); // must use super, as we override
|
||||
}
|
||||
|
||||
public String getDomain() {
|
||||
return this.domain;
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package com.baeldung.loginextrafieldscustom;
|
||||
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
|
||||
/**
|
||||
* The plaintext password used to perform
|
||||
* PasswordEncoder#matches(CharSequence, String)} on when the user is
|
||||
* not found to avoid SEC-2056.
|
||||
*/
|
||||
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
|
||||
|
||||
private PasswordEncoder passwordEncoder;
|
||||
private CustomUserDetailsService userDetailsService;
|
||||
|
||||
/**
|
||||
* The password used to perform
|
||||
* {@link PasswordEncoder#matches(CharSequence, String)} on when the user is
|
||||
* not found to avoid SEC-2056. This is necessary, because some
|
||||
* {@link PasswordEncoder} implementations will short circuit if the password is not
|
||||
* in a valid format.
|
||||
*/
|
||||
private String userNotFoundEncodedPassword;
|
||||
|
||||
public CustomUserDetailsAuthenticationProvider(PasswordEncoder passwordEncoder, CustomUserDetailsService userDetailsService) {
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
|
||||
if (authentication.getCredentials() == null) {
|
||||
logger.debug("Authentication failed: no credentials provided");
|
||||
throw new BadCredentialsException(
|
||||
messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
|
||||
String presentedPassword = authentication.getCredentials()
|
||||
.toString();
|
||||
|
||||
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
|
||||
logger.debug("Authentication failed: password does not match stored value");
|
||||
throw new BadCredentialsException(
|
||||
messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAfterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
|
||||
this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
CustomAuthenticationToken auth = (CustomAuthenticationToken) authentication;
|
||||
UserDetails loadedUser;
|
||||
|
||||
try {
|
||||
loadedUser = this.userDetailsService.loadUserByUsernameAndDomain(auth.getPrincipal()
|
||||
.toString(), auth.getDomain());
|
||||
} catch (UsernameNotFoundException notFound) {
|
||||
if (authentication.getCredentials() != null) {
|
||||
String presentedPassword = authentication.getCredentials()
|
||||
.toString();
|
||||
passwordEncoder.matches(presentedPassword, userNotFoundEncodedPassword);
|
||||
}
|
||||
throw notFound;
|
||||
} catch (Exception repositoryProblem) {
|
||||
throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
|
||||
}
|
||||
|
||||
if (loadedUser == null) {
|
||||
throw new InternalAuthenticationServiceException("UserDetailsService returned null, "
|
||||
+ "which is an interface contract violation");
|
||||
}
|
||||
return loadedUser;
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.baeldung.loginextrafieldscustom;
|
||||
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
public interface CustomUserDetailsService {
|
||||
|
||||
UserDetails loadUserByUsernameAndDomain(String username, String domain) throws UsernameNotFoundException;
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user