Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
b7ab9c3dea
@ -2,11 +2,12 @@ package com.baeldung.spliteratorAPI;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Spliterator;
|
import java.util.Spliterator;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class RelatedAuthorSpliterator implements Spliterator<Author> {
|
public class RelatedAuthorSpliterator implements Spliterator<Author> {
|
||||||
private final List<Author> list;
|
private final List<Author> list;
|
||||||
private int current = 0;
|
AtomicInteger current = new AtomicInteger();
|
||||||
|
|
||||||
public RelatedAuthorSpliterator(List<Author> list) {
|
public RelatedAuthorSpliterator(List<Author> list) {
|
||||||
this.list = list;
|
this.list = list;
|
||||||
@ -14,21 +15,21 @@ public class RelatedAuthorSpliterator implements Spliterator<Author> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tryAdvance(Consumer<? super Author> action) {
|
public boolean tryAdvance(Consumer<? super Author> action) {
|
||||||
action.accept(list.get(current++));
|
|
||||||
return current < list.size();
|
action.accept(list.get(current.getAndIncrement()));
|
||||||
|
return current.get() < list.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Spliterator<Author> trySplit() {
|
public Spliterator<Author> trySplit() {
|
||||||
int currentSize = list.size() - current;
|
int currentSize = list.size() - current.get();
|
||||||
if (currentSize < 10) {
|
if (currentSize < 10) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (int splitPos = currentSize / 2 + current; splitPos < list.size(); splitPos++) {
|
for (int splitPos = currentSize / 2 + current.intValue(); splitPos < list.size(); splitPos++) {
|
||||||
if (list.get(splitPos)
|
if (list.get(splitPos).getRelatedArticleId() == 0) {
|
||||||
.getRelatedArticleId() == 0) {
|
Spliterator<Author> spliterator = new RelatedAuthorSpliterator(list.subList(current.get(), splitPos));
|
||||||
Spliterator<Author> spliterator = new RelatedAuthorSpliterator(list.subList(current, splitPos));
|
current.set(splitPos);
|
||||||
current = splitPos;
|
|
||||||
return spliterator;
|
return spliterator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,11 +38,12 @@ public class RelatedAuthorSpliterator implements Spliterator<Author> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long estimateSize() {
|
public long estimateSize() {
|
||||||
return list.size() - current;
|
return list.size() - current.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int characteristics() {
|
public int characteristics() {
|
||||||
return SIZED + CONCURRENT;
|
return CONCURRENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.baeldung.concurrent.waitandnotify;
|
package com.baeldung.concurrent.waitandnotify;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
public class Data {
|
public class Data {
|
||||||
private String packet;
|
private String packet;
|
||||||
|
|
||||||
@ -11,7 +13,9 @@ public class Data {
|
|||||||
while (transfer) {
|
while (transfer) {
|
||||||
try {
|
try {
|
||||||
wait();
|
wait();
|
||||||
} catch (InterruptedException e) {}
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("Thread Interrupted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
transfer = true;
|
transfer = true;
|
||||||
|
|
||||||
@ -23,7 +27,9 @@ public class Data {
|
|||||||
while (!transfer) {
|
while (!transfer) {
|
||||||
try {
|
try {
|
||||||
wait();
|
wait();
|
||||||
} catch (InterruptedException e) {}
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("Thread Interrupted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
transfer = false;
|
transfer = false;
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@ public class Receiver implements Runnable {
|
|||||||
//Thread.sleep() to mimic heavy server-side processing
|
//Thread.sleep() to mimic heavy server-side processing
|
||||||
try {
|
try {
|
||||||
Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000));
|
Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000));
|
||||||
} catch (InterruptedException e) {}
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("Thread Interrupted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,7 +24,9 @@ public class Sender implements Runnable {
|
|||||||
//Thread.sleep() to mimic heavy server-side processing
|
//Thread.sleep() to mimic heavy server-side processing
|
||||||
try {
|
try {
|
||||||
Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000));
|
Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000));
|
||||||
} catch (InterruptedException e) {}
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("Thread Interrupted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -126,3 +126,4 @@
|
|||||||
- [A Guide to Inner Interfaces in Java](http://www.baeldung.com/java-inner-interfaces)
|
- [A Guide to Inner Interfaces in Java](http://www.baeldung.com/java-inner-interfaces)
|
||||||
- [Polymorphism in Java](http://www.baeldung.com/java-polymorphism)
|
- [Polymorphism in Java](http://www.baeldung.com/java-polymorphism)
|
||||||
- [Recursion In Java](http://www.baeldung.com/java-recursion)
|
- [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>
|
<artifactId>kotlin-stdlib-jre8</artifactId>
|
||||||
<version>${kotlin-stdlib.version}</version>
|
<version>${kotlin-stdlib.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>khttp</groupId>
|
||||||
|
<artifactId>khttp</artifactId>
|
||||||
|
<version>0.1.0</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jetbrains.kotlin</groupId>
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
<artifactId>kotlin-test-junit</artifactId>
|
<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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
java-rmi/pom.xml
Normal file
20
java-rmi/pom.xml
Normal file
@ -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>
|
36
java-rmi/src/main/java/com/baeldung/rmi/Message.java
Normal file
36
java-rmi/src/main/java/com/baeldung/rmi/Message.java
Normal file
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -194,6 +194,11 @@
|
|||||||
<artifactId>jetty-servlet</artifactId>
|
<artifactId>jetty-servlet</artifactId>
|
||||||
<version>${jetty.version}</version>
|
<version>${jetty.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-webapp</artifactId>
|
||||||
|
<version>${jetty.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>rome</groupId>
|
<groupId>rome</groupId>
|
||||||
<artifactId>rome</artifactId>
|
<artifactId>rome</artifactId>
|
||||||
@ -741,12 +746,11 @@
|
|||||||
<assertj.version>3.6.2</assertj.version>
|
<assertj.version>3.6.2</assertj.version>
|
||||||
<jsonassert.version>1.5.0</jsonassert.version>
|
<jsonassert.version>1.5.0</jsonassert.version>
|
||||||
<javers.version>3.1.0</javers.version>
|
<javers.version>3.1.0</javers.version>
|
||||||
<jetty.version>9.4.3.v20170317</jetty.version>
|
|
||||||
<httpclient.version>4.5.3</httpclient.version>
|
<httpclient.version>4.5.3</httpclient.version>
|
||||||
<commons.io.version>2.5</commons.io.version>
|
<commons.io.version>2.5</commons.io.version>
|
||||||
<commons.dbutils.version>1.6</commons.dbutils.version>
|
<commons.dbutils.version>1.6</commons.dbutils.version>
|
||||||
<h2.version>1.4.196</h2.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>
|
<httpclient.version>4.5.3</httpclient.version>
|
||||||
<commons.io.version>2.5</commons.io.version>
|
<commons.io.version>2.5</commons.io.version>
|
||||||
<flink.version>1.2.0</flink.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");
|
headers.put("Content-Type", "application/json");
|
||||||
|
|
||||||
return builder
|
return builder
|
||||||
.given("test GET ")
|
.given("test GET")
|
||||||
.uponReceiving("GET REQUEST")
|
.uponReceiving("GET REQUEST")
|
||||||
.path("/")
|
.path("/pact")
|
||||||
.method("GET")
|
.method("GET")
|
||||||
.willRespondWith()
|
.willRespondWith()
|
||||||
.status(200)
|
.status(200)
|
||||||
@ -45,11 +45,9 @@ public class PactConsumerDrivenContractUnitTest {
|
|||||||
.method("POST")
|
.method("POST")
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.body("{\"name\": \"Michael\"}")
|
.body("{\"name\": \"Michael\"}")
|
||||||
.path("/create")
|
.path("/pact")
|
||||||
.willRespondWith()
|
.willRespondWith()
|
||||||
.status(201)
|
.status(201)
|
||||||
.headers(headers)
|
|
||||||
.body("")
|
|
||||||
.toPact();
|
.toPact();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +57,7 @@ public class PactConsumerDrivenContractUnitTest {
|
|||||||
public void givenGet_whenSendRequest_shouldReturn200WithProperHeaderAndBody() {
|
public void givenGet_whenSendRequest_shouldReturn200WithProperHeaderAndBody() {
|
||||||
//when
|
//when
|
||||||
ResponseEntity<String> response
|
ResponseEntity<String> response
|
||||||
= new RestTemplate().getForEntity(mockProvider.getUrl(), String.class);
|
= new RestTemplate().getForEntity(mockProvider.getUrl() + "/pact", String.class);
|
||||||
|
|
||||||
//then
|
//then
|
||||||
assertThat(response.getStatusCode().value()).isEqualTo(200);
|
assertThat(response.getStatusCode().value()).isEqualTo(200);
|
||||||
@ -73,7 +71,7 @@ public class PactConsumerDrivenContractUnitTest {
|
|||||||
|
|
||||||
//when
|
//when
|
||||||
ResponseEntity<String> postResponse = new RestTemplate().exchange(
|
ResponseEntity<String> postResponse = new RestTemplate().exchange(
|
||||||
mockProvider.getUrl() + "/create",
|
mockProvider.getUrl() + "/pact",
|
||||||
HttpMethod.POST,
|
HttpMethod.POST,
|
||||||
new HttpEntity<>(jsonBody, httpHeaders),
|
new HttpEntity<>(jsonBody, httpHeaders),
|
||||||
String.class
|
String.class
|
||||||
|
BIN
libraries/src/test/resources/jetty-embedded-demo-app.war
Normal file
BIN
libraries/src/test/resources/jetty-embedded-demo-app.war
Normal file
Binary file not shown.
1
pom.xml
1
pom.xml
@ -88,6 +88,7 @@
|
|||||||
<!-- <module>persistence-modules/java-cassandra</module> -->
|
<!-- <module>persistence-modules/java-cassandra</module> -->
|
||||||
<module>vavr</module>
|
<module>vavr</module>
|
||||||
<module>java-lite</module>
|
<module>java-lite</module>
|
||||||
|
<module>java-rmi</module>
|
||||||
<module>java-vavr-stream</module>
|
<module>java-vavr-stream</module>
|
||||||
<module>javax-servlets</module>
|
<module>javax-servlets</module>
|
||||||
<module>javaxval</module>
|
<module>javaxval</module>
|
||||||
|
@ -2,22 +2,25 @@ package com.baeldung.reactive.websocket;
|
|||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
import org.springframework.web.reactive.socket.WebSocketMessage;
|
import org.springframework.web.reactive.socket.WebSocketMessage;
|
||||||
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
|
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
|
||||||
import org.springframework.web.reactive.socket.client.WebSocketClient;
|
import org.springframework.web.reactive.socket.client.WebSocketClient;
|
||||||
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
public class ReactiveJavaClientWebSocket {
|
public class ReactiveJavaClientWebSocket {
|
||||||
public static void main(String[] args) throws InterruptedException {
|
public static void main(String[] args) throws InterruptedException {
|
||||||
|
|
||||||
WebSocketClient client = new ReactorNettyWebSocketClient();
|
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()
|
.thenMany(session.receive()
|
||||||
.map(WebSocketMessage::getPayloadAsText)
|
.map(WebSocketMessage::getPayloadAsText)
|
||||||
.log())
|
.log())
|
||||||
.then())
|
.then())
|
||||||
.block(Duration.ofSeconds(10L));
|
.block(Duration.ofSeconds(10L));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,71 +1,41 @@
|
|||||||
package com.baeldung.reactive.websocket;
|
package com.baeldung.reactive.websocket;
|
||||||
|
|
||||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||||
import org.springframework.web.reactive.socket.WebSocketMessage;
|
import org.springframework.web.reactive.socket.WebSocketMessage;
|
||||||
|
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import java.time.Duration;
|
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
|
@Component
|
||||||
public class ReactiveWebSocketHandler implements WebSocketHandler {
|
public class ReactiveWebSocketHandler implements WebSocketHandler {
|
||||||
|
|
||||||
private Flux<Event> eventFlux;
|
private static final ObjectMapper json = new ObjectMapper();
|
||||||
private Flux<Event> intervalFlux;
|
|
||||||
|
|
||||||
/**
|
private Flux<String> eventFlux = Flux.generate(sink -> {
|
||||||
* Here we prepare a Flux that will emit a message every second
|
Event event = new Event(randomUUID().toString(), now().toString());
|
||||||
*/
|
try {
|
||||||
@PostConstruct
|
sink.next(json.writeValueAsString(event));
|
||||||
private void init() throws InterruptedException {
|
} catch (JsonProcessingException e) {
|
||||||
|
sink.error(e);
|
||||||
eventFlux = Flux.generate(e -> {
|
}
|
||||||
Event event = new Event(UUID.randomUUID()
|
|
||||||
.toString(),
|
|
||||||
LocalDateTime.now()
|
|
||||||
.toString());
|
|
||||||
e.next(event);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
intervalFlux = Flux.interval(Duration.ofMillis(1000L))
|
private Flux<String> intervalFlux = Flux.interval(Duration.ofMillis(1000L))
|
||||||
.zipWith(eventFlux, (time, event) -> event);
|
.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
|
@Override
|
||||||
public Mono<Void> handle(WebSocketSession webSocketSession) {
|
public Mono<Void> handle(WebSocketSession webSocketSession) {
|
||||||
ObjectMapper json = new ObjectMapper();
|
return webSocketSession.send(intervalFlux
|
||||||
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))
|
.map(webSocketSession::textMessage))
|
||||||
|
|
||||||
.and(webSocketSession.receive()
|
.and(webSocketSession.receive()
|
||||||
.map(WebSocketMessage::getPayloadAsText)
|
.map(WebSocketMessage::getPayloadAsText).log());
|
||||||
.log());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.baeldung.loginextrafieldscustom;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service("userDetailsService")
|
||||||
|
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
|
||||||
|
|
||||||
|
private UserRepository userRepository;
|
||||||
|
|
||||||
|
public CustomUserDetailsServiceImpl(UserRepository userRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsernameAndDomain(String username, String domain) throws UsernameNotFoundException {
|
||||||
|
if (StringUtils.isAnyBlank(username, domain)) {
|
||||||
|
throw new UsernameNotFoundException("Username and domain must be provided");
|
||||||
|
}
|
||||||
|
User user = userRepository.findUser(username, domain);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UsernameNotFoundException(
|
||||||
|
String.format("Username not found for domain, username=%s, domain=%s",
|
||||||
|
username, domain));
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.baeldung.securityextrafields;
|
package com.baeldung.loginextrafieldscustom;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
@ -1,13 +1,13 @@
|
|||||||
package com.baeldung.securityextrafields;
|
package com.baeldung.loginextrafieldscustom;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class SpringExtraLoginFieldsApplication {
|
public class ExtraLoginFieldsApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(SpringExtraLoginFieldsApplication.class, args);
|
SpringApplication.run(ExtraLoginFieldsApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package com.baeldung.loginextrafieldscustom;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.PropertySource;
|
||||||
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@PropertySource("classpath:/application-extrafields.properties")
|
||||||
|
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CustomUserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
|
||||||
|
http
|
||||||
|
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||||
|
.authorizeRequests()
|
||||||
|
.antMatchers("/css/**", "/index").permitAll()
|
||||||
|
.antMatchers("/user/**").authenticated()
|
||||||
|
.and()
|
||||||
|
.formLogin().loginPage("/login")
|
||||||
|
.and()
|
||||||
|
.logout()
|
||||||
|
.logoutUrl("/logout");
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomAuthenticationFilter authenticationFilter() throws Exception {
|
||||||
|
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
|
||||||
|
filter.setAuthenticationManager(authenticationManagerBean());
|
||||||
|
filter.setAuthenticationFailureHandler(failureHandler());
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
auth.authenticationProvider(authProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationProvider authProvider() {
|
||||||
|
CustomUserDetailsAuthenticationProvider provider
|
||||||
|
= new CustomUserDetailsAuthenticationProvider(passwordEncoder(), userDetailsService);
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleUrlAuthenticationFailureHandler failureHandler() {
|
||||||
|
return new SimpleUrlAuthenticationFailureHandler("/login?error=true");
|
||||||
|
}
|
||||||
|
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.baeldung.securityextrafields;
|
package com.baeldung.loginextrafieldscustom;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ public class User extends org.springframework.security.core.userdetails.User {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final String domain;
|
private String domain;
|
||||||
|
|
||||||
public User(String username, String domain, String password, boolean enabled,
|
public User(String username, String domain, String password, boolean enabled,
|
||||||
boolean accountNonExpired, boolean credentialsNonExpired,
|
boolean accountNonExpired, boolean credentialsNonExpired,
|
@ -1,4 +1,4 @@
|
|||||||
package com.baeldung.securityextrafields;
|
package com.baeldung.loginextrafieldscustom;
|
||||||
|
|
||||||
public interface UserRepository {
|
public interface UserRepository {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.baeldung.securityextrafields;
|
package com.baeldung.loginextrafieldscustom;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.baeldung.loginextrafieldssimple;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class ExtraLoginFieldsApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(ExtraLoginFieldsApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.baeldung.securityextrafields;
|
package com.baeldung.loginextrafieldssimple;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.PropertySource;
|
import org.springframework.context.annotation.PropertySource;
|
||||||
@ -36,8 +36,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
.logoutUrl("/logout");
|
.logoutUrl("/logout");
|
||||||
}
|
}
|
||||||
|
|
||||||
public CustomAuthenticationFilter authenticationFilter() throws Exception {
|
public SimpleAuthenticationFilter authenticationFilter() throws Exception {
|
||||||
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
|
SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter();
|
||||||
filter.setAuthenticationManager(authenticationManagerBean());
|
filter.setAuthenticationManager(authenticationManagerBean());
|
||||||
filter.setAuthenticationFailureHandler(failureHandler());
|
filter.setAuthenticationFailureHandler(failureHandler());
|
||||||
return filter;
|
return filter;
|
@ -1,4 +1,4 @@
|
|||||||
package com.baeldung.securityextrafields;
|
package com.baeldung.loginextrafieldssimple;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
@ -9,7 +9,7 @@ import org.springframework.security.core.Authentication;
|
|||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
|
public class SimpleAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
|
||||||
|
|
||||||
public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain";
|
public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain";
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.baeldung.securityextrafields;
|
package com.baeldung.loginextrafieldssimple;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
@ -7,11 +7,11 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service("userDetailsService")
|
@Service("userDetailsService")
|
||||||
public class CustomUserDetailsService implements UserDetailsService {
|
public class SimpleUserDetailsService implements UserDetailsService {
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
public CustomUserDetailsService(UserRepository userRepository) {
|
public SimpleUserDetailsService(UserRepository userRepository) {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.baeldung.loginextrafieldssimple;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository("userRepository")
|
||||||
|
public class SimpleUserRepository implements UserRepository {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User findUser(String username, String domain) {
|
||||||
|
if (StringUtils.isAnyBlank(username, domain)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
|
||||||
|
User user = new User(username, domain,
|
||||||
|
"$2a$10$U3GhSMpsMSOE8Kqsbn58/edxDBKlVuYMh7qk/7ErApYFjJzi2VG5K", true,
|
||||||
|
true, true, true, authorities);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.baeldung.loginextrafieldssimple;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
public class User extends org.springframework.security.core.userdetails.User {
|
||||||
|
|
||||||
|
private String domain;
|
||||||
|
|
||||||
|
public User(String username, String domain, String password, boolean enabled,
|
||||||
|
boolean accountNonExpired, boolean credentialsNonExpired,
|
||||||
|
boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
|
||||||
|
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
|
||||||
|
this.domain = domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomain() {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.baeldung.loginextrafieldssimple;
|
||||||
|
|
||||||
|
public interface UserRepository {
|
||||||
|
|
||||||
|
public User findUser(String username, String domain);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.baeldung.loginextrafieldssimple;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class WebController {
|
||||||
|
|
||||||
|
@RequestMapping("/")
|
||||||
|
public String root() {
|
||||||
|
return "redirect:/index";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/index")
|
||||||
|
public String index(Model model) {
|
||||||
|
getDomain().ifPresent(d -> {
|
||||||
|
model.addAttribute("domain", d);
|
||||||
|
});
|
||||||
|
return "index";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/user/index")
|
||||||
|
public String userIndex(Model model) {
|
||||||
|
getDomain().ifPresent(d -> {
|
||||||
|
model.addAttribute("domain", d);
|
||||||
|
});
|
||||||
|
return "user/index";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/login")
|
||||||
|
public String login() {
|
||||||
|
return "login";
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<String> getDomain() {
|
||||||
|
Authentication auth = SecurityContextHolder.getContext()
|
||||||
|
.getAuthentication();
|
||||||
|
String domain = null;
|
||||||
|
if (auth != null && !auth.getClass().equals(AnonymousAuthenticationToken.class)) {
|
||||||
|
User user = (User) auth.getPrincipal();
|
||||||
|
domain = user.getDomain();
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(domain);
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,8 @@
|
|||||||
body {
|
|
||||||
font-family: sans;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.error {
|
p.error {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.logout {
|
div.logout {
|
||||||
float: right;
|
margin-right: 2em;;
|
||||||
}
|
|
||||||
|
|
||||||
.formfield {
|
|
||||||
margin: 0.5em;
|
|
||||||
padding: 0.3em;
|
|
||||||
}
|
}
|
@ -1,24 +1,32 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4" lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Spring Security - Login With Extra Fields</title>
|
<title>Spring Security with Extra Fields</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="author" content="">
|
||||||
|
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
|
||||||
|
<link href="http://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
|
||||||
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
|
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div th:fragment="logout" class="logout" sec:authorize="isAuthenticated()">
|
<div class="container">
|
||||||
Logged in user: <span sec:authentication="name"></span> |
|
<div class="logout float-right" th:fragment="logout" sec:authorize="isAuthenticated()">
|
||||||
domain: <span th:text="${domain}">Some Domain</span>
|
<p>Logged in: <span sec:authentication="name"></span> | <span th:text="${domain}">Some Domain</span>
|
||||||
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<form action="#" th:action="@{/logout}" method="post">
|
<form action="#" th:action="@{/logout}" method="post">
|
||||||
<input type="submit" value="Logout" />
|
<button class="btn btn-sm btn-primary btn-block" type="submit">Logout</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h1>Hello Spring Security</h1>
|
|
||||||
|
<h2>Hello Spring Security</h2>
|
||||||
<p>This is an unsecured page, but you can access the secured pages after authenticating.</p>
|
<p>This is an unsecured page, but you can access the secured pages after authenticating.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Go to the <a href="/user/index" th:href="@{/user/index}">secured pages</a></li>
|
<li>Go to the <a href="/user/index" th:href="@{/user/index}">secured pages</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,23 +1,36 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Login page</title>
|
<title>Login page</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="author" content="">
|
||||||
|
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
|
||||||
|
<link href="http://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
|
||||||
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
|
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Login page</h1>
|
<div class="container">
|
||||||
|
<form class="form-signin" th:action="@{/login}" method="post">
|
||||||
|
<h2 class="form-signin-heading">Please sign in</h2>
|
||||||
<p>Example: user / domain / password</p>
|
<p>Example: user / domain / password</p>
|
||||||
<p th:if="${param.error}" class="error">Invalid user, password, or domain</p>
|
<p th:if="${param.error}" class="error">Invalid user, password, or domain</p>
|
||||||
<form th:action="@{/login}" method="post">
|
<p>
|
||||||
<label for="username">Username</label>:
|
<label for="username" class="sr-only">Username</label>
|
||||||
<input class="formfield" type="text" id="username" name="username" autofocus="autofocus" /> <br />
|
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus/>
|
||||||
<label for="domain">Domain</label>:
|
</p>
|
||||||
<input class="formfield" type="text" id="domain" name="domain" /> <br />
|
<p>
|
||||||
<label for="password">Password</label>:
|
<label for="domain" class="sr-only">Domain</label>
|
||||||
<input class="formfield" type="password" id="password" name="password" /> <br />
|
<input type="text" id="domain" name="domain" class="form-control" placeholder="Domain" required autofocus/>
|
||||||
<input type="submit" value="Log in" />
|
</p>
|
||||||
</form>
|
<p>
|
||||||
|
<label for="password" class="sr-only">Password</label>
|
||||||
|
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required autofocus/>
|
||||||
|
</p>
|
||||||
|
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button><br/>
|
||||||
<p><a href="/index" th:href="@{/index}">Back to home page</a></p>
|
<p><a href="/index" th:href="@{/index}">Back to home page</a></p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Spring Security - Login With Extra Fields</title>
|
<title>Secured Page</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="author" content="">
|
||||||
|
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
|
||||||
|
<link href="http://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
|
||||||
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
|
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div class="container">
|
||||||
<div th:replace="index::logout"></div>
|
<div th:replace="index::logout"></div>
|
||||||
<h1>This is a secured page!</h1>
|
<h2>This is a secured page!</h2>
|
||||||
<p><a href="/index" th:href="@{/index}">Back to home page</a></p>
|
<p><a href="/index" th:href="@{/index}">Back to home page</a></p>
|
||||||
</body>
|
</div>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.baeldung.loginextrafields;
|
||||||
|
|
||||||
|
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.web.FilterChainProxy;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
public abstract class AbstractExtraLoginFieldsTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FilterChainProxy springSecurityFilterChain;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WebApplicationContext wac;
|
||||||
|
|
||||||
|
protected MockMvc mockMvc;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
|
||||||
|
.apply(springSecurity(springSecurityFilterChain))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenRootPathAccess_thenRedirectToIndex() throws Exception {
|
||||||
|
this.mockMvc.perform(get("/"))
|
||||||
|
.andExpect(status().is3xxRedirection())
|
||||||
|
.andExpect(redirectedUrlPattern("/index*"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception {
|
||||||
|
this.mockMvc.perform(get("/user/index"))
|
||||||
|
.andExpect(status().is3xxRedirection())
|
||||||
|
.andExpect(redirectedUrlPattern("**/login"));
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,7 @@
|
|||||||
package com.baeldung.securityextrafields;
|
package com.baeldung.loginextrafields;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;
|
||||||
@ -11,57 +10,26 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.mock.web.MockHttpSession;
|
import org.springframework.mock.web.MockHttpSession;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.web.FilterChainProxy;
|
|
||||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
|
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import com.baeldung.loginextrafieldscustom.ExtraLoginFieldsApplication;
|
||||||
|
import com.baeldung.loginextrafieldscustom.User;
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@SpringJUnitWebConfig
|
@SpringJUnitWebConfig
|
||||||
@SpringBootTest(classes = SpringExtraLoginFieldsApplication.class)
|
@SpringBootTest(classes = ExtraLoginFieldsApplication.class)
|
||||||
public class SecurityExtraFieldsTest {
|
public class LoginFieldsFullTest extends AbstractExtraLoginFieldsTest {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private FilterChainProxy springSecurityFilterChain;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private WebApplicationContext wac;
|
|
||||||
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() {
|
|
||||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
|
|
||||||
.apply(springSecurity(springSecurityFilterChain)).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenRootPathAccess_thenRedirectToIndex() throws Exception {
|
|
||||||
this.mockMvc.perform(get("/"))
|
|
||||||
.andExpect(status().is3xxRedirection())
|
|
||||||
.andExpect(redirectedUrlPattern("/index*"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception {
|
|
||||||
this.mockMvc.perform(get("/user/index"))
|
|
||||||
.andExpect(status().is3xxRedirection())
|
|
||||||
.andExpect(redirectedUrlPattern("**/login"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception {
|
public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception {
|
||||||
@ -100,4 +68,5 @@ public class SecurityExtraFieldsTest {
|
|||||||
Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
|
Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
|
||||||
return new User("myusername", "mydomain", "password", true, true, true, true, authorities);
|
return new User("myusername", "mydomain", "password", true, true, true, true, authorities);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package com.baeldung.loginextrafields;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.mock.web.MockHttpSession;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
|
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||||
|
|
||||||
|
import com.baeldung.loginextrafieldssimple.ExtraLoginFieldsApplication;
|
||||||
|
import com.baeldung.loginextrafieldssimple.User;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringJUnitWebConfig
|
||||||
|
@SpringBootTest(classes = ExtraLoginFieldsApplication.class)
|
||||||
|
public class LoginFieldsSimpleTest extends AbstractExtraLoginFieldsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception {
|
||||||
|
MockHttpServletRequestBuilder securedResourceAccess = get("/user/index");
|
||||||
|
MvcResult unauthenticatedResult = mockMvc.perform(securedResourceAccess)
|
||||||
|
.andExpect(status().is3xxRedirection())
|
||||||
|
.andReturn();
|
||||||
|
|
||||||
|
MockHttpSession session = (MockHttpSession) unauthenticatedResult.getRequest()
|
||||||
|
.getSession();
|
||||||
|
String loginUrl = unauthenticatedResult.getResponse()
|
||||||
|
.getRedirectedUrl();
|
||||||
|
|
||||||
|
User user = getUser();
|
||||||
|
|
||||||
|
mockMvc.perform(post(loginUrl)
|
||||||
|
.param("username", user.getUsername())
|
||||||
|
.param("password", user.getPassword())
|
||||||
|
.param("domain", user.getDomain())
|
||||||
|
.session(session)
|
||||||
|
.with(csrf()))
|
||||||
|
.andExpect(status().is3xxRedirection())
|
||||||
|
.andExpect(redirectedUrlPattern("**/user/index"))
|
||||||
|
.andReturn();
|
||||||
|
|
||||||
|
mockMvc.perform(securedResourceAccess.session(session))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
SecurityContext securityContext
|
||||||
|
= (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
|
||||||
|
Authentication auth = securityContext.getAuthentication();
|
||||||
|
assertEquals(((User)auth.getPrincipal()).getDomain(), user.getDomain());
|
||||||
|
}
|
||||||
|
|
||||||
|
private User getUser() {
|
||||||
|
Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
|
||||||
|
return new User("myusername", "mydomain", "password", true, true, true, true, authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -37,6 +37,7 @@ public class BasicAuthConfigurationIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
public void whenLoggedUserRequestsHomePage_ThenSuccess() throws IllegalStateException, IOException {
|
public void whenLoggedUserRequestsHomePage_ThenSuccess() throws IllegalStateException, IOException {
|
||||||
ResponseEntity<String> response = restTemplate.getForEntity(base.toString(), String.class);
|
ResponseEntity<String> response = restTemplate.getForEntity(base.toString(), String.class);
|
||||||
|
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
assertTrue(response
|
assertTrue(response
|
||||||
.getBody()
|
.getBody()
|
||||||
@ -47,6 +48,7 @@ public class BasicAuthConfigurationIntegrationTest {
|
|||||||
public void whenUserWithWrongCredentialsRequestsHomePage_ThenUnauthorizedPage() throws IllegalStateException, IOException {
|
public void whenUserWithWrongCredentialsRequestsHomePage_ThenUnauthorizedPage() throws IllegalStateException, IOException {
|
||||||
restTemplate = new TestRestTemplate("user", "wrongpassword");
|
restTemplate = new TestRestTemplate("user", "wrongpassword");
|
||||||
ResponseEntity<String> response = restTemplate.getForEntity(base.toString(), String.class);
|
ResponseEntity<String> response = restTemplate.getForEntity(base.toString(), String.class);
|
||||||
|
|
||||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||||
assertTrue(response
|
assertTrue(response
|
||||||
.getBody()
|
.getBody()
|
||||||
|
@ -2,10 +2,7 @@ package com.baeldung.springbootsecurity.oauth2server;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
|
||||||
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
|
|
||||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||||
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
|
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
|
||||||
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
||||||
@ -13,7 +10,6 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
|||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
import static java.lang.String.format;
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||||
@ -21,54 +17,35 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
|||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class)
|
@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class)
|
||||||
@ActiveProfiles("authz")
|
@ActiveProfiles("authz")
|
||||||
public class CustomConfigAuthorizationServerIntegrationTest {
|
public class CustomConfigAuthorizationServerIntegrationTest extends OAuth2IntegrationTestSupport {
|
||||||
|
|
||||||
@Value("${local.server.port}") protected int port;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() {
|
public void givenOAuth2Context_whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() {
|
||||||
ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails();
|
ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("baeldung", singletonList("read"));
|
||||||
resourceDetails.setClientId("baeldung");
|
OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails);
|
||||||
resourceDetails.setClientSecret("baeldung");
|
|
||||||
resourceDetails.setScope(singletonList("read"));
|
|
||||||
DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext();
|
|
||||||
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext);
|
|
||||||
restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter()));
|
|
||||||
OAuth2AccessToken accessToken = restTemplate.getAccessToken();
|
OAuth2AccessToken accessToken = restTemplate.getAccessToken();
|
||||||
|
|
||||||
assertNotNull(accessToken);
|
assertNotNull(accessToken);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = OAuth2AccessDeniedException.class)
|
@Test(expected = OAuth2AccessDeniedException.class)
|
||||||
public void whenAccessTokenIsRequestedWithInvalidException_ThenExceptionIsThrown() {
|
public void givenOAuth2Context_whenAccessTokenIsRequestedWithInvalidException_ThenExceptionIsThrown() {
|
||||||
ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails();
|
ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("baeldung", singletonList("write"));
|
||||||
resourceDetails.setClientId("baeldung");
|
OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails);
|
||||||
resourceDetails.setClientSecret("baeldung");
|
|
||||||
resourceDetails.setScope(singletonList("write"));
|
|
||||||
DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext();
|
|
||||||
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext);
|
|
||||||
restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter()));
|
|
||||||
restTemplate.getAccessToken();
|
restTemplate.getAccessToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenAccessTokenIsRequestedByClientWithWriteScope_ThenAccessTokenIsNotNull() {
|
public void givenOAuth2Context_whenAccessTokenIsRequestedByClientWithWriteScope_ThenAccessTokenIsNotNull() {
|
||||||
ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails();
|
ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("baeldung-admin", singletonList("write"));
|
||||||
resourceDetails.setClientId("baeldung-admin");
|
OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails);
|
||||||
resourceDetails.setClientSecret("baeldung");
|
|
||||||
resourceDetails.setScope(singletonList("write"));
|
|
||||||
DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext();
|
|
||||||
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext);
|
|
||||||
restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter()));
|
|
||||||
OAuth2AccessToken accessToken = restTemplate.getAccessToken();
|
|
||||||
assertNotNull(accessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClientCredentialsResourceDetails getClientCredentialsResourceDetails() {
|
OAuth2AccessToken accessToken = restTemplate.getAccessToken();
|
||||||
ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
|
|
||||||
resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port));
|
assertNotNull(accessToken);
|
||||||
resourceDetails.setGrantType("client_credentials");
|
|
||||||
return resourceDetails;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,40 +2,28 @@ package com.baeldung.springbootsecurity.oauth2server;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
|
||||||
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
|
|
||||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||||
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
||||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
import static java.lang.String.format;
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class,
|
@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class,
|
||||||
properties = { "security.oauth2.client.client-id=client", "security.oauth2.client.client-secret=secret" })
|
properties = { "security.oauth2.client.client-id=client", "security.oauth2.client.client-secret=baeldung" })
|
||||||
public class DefaultConfigAuthorizationServerIntegrationTest {
|
public class DefaultConfigAuthorizationServerIntegrationTest extends OAuth2IntegrationTestSupport {
|
||||||
|
|
||||||
@Value("${local.server.port}") protected int port;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() {
|
public void givenOAuth2Context_whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() {
|
||||||
ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
|
ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("client", asList("read", "write"));
|
||||||
resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port));
|
OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails);
|
||||||
resourceDetails.setClientId("client");
|
|
||||||
resourceDetails.setClientSecret("secret");
|
|
||||||
resourceDetails.setGrantType("client_credentials");
|
|
||||||
resourceDetails.setScope(asList("read", "write"));
|
|
||||||
DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext();
|
|
||||||
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext);
|
|
||||||
restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter()));
|
|
||||||
OAuth2AccessToken accessToken = restTemplate.getAccessToken();
|
OAuth2AccessToken accessToken = restTemplate.getAccessToken();
|
||||||
|
|
||||||
assertNotNull(accessToken);
|
assertNotNull(accessToken);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.baeldung.springbootsecurity.oauth2server;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
|
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||||
|
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
|
||||||
|
public class OAuth2IntegrationTestSupport {
|
||||||
|
|
||||||
|
@Value("${local.server.port}") protected int port;
|
||||||
|
|
||||||
|
protected ClientCredentialsResourceDetails getClientCredentialsResourceDetails(final String clientId, final List<String> scopes) {
|
||||||
|
ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
|
||||||
|
resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port));
|
||||||
|
resourceDetails.setClientId(clientId);
|
||||||
|
resourceDetails.setClientSecret("baeldung");
|
||||||
|
resourceDetails.setScope(scopes);
|
||||||
|
resourceDetails.setGrantType("client_credentials");
|
||||||
|
return resourceDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OAuth2RestTemplate getOAuth2RestTemplate(final ClientCredentialsResourceDetails resourceDetails) {
|
||||||
|
DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext();
|
||||||
|
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext);
|
||||||
|
restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter()));
|
||||||
|
return restTemplate;
|
||||||
|
}
|
||||||
|
}
|
@ -167,6 +167,12 @@
|
|||||||
<groupId>org.apache.activemq</groupId>
|
<groupId>org.apache.activemq</groupId>
|
||||||
<artifactId>artemis-server</artifactId>
|
<artifactId>artemis-server</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.rometools</groupId>
|
||||||
|
<artifactId>rome</artifactId>
|
||||||
|
<version>${rome.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@ -276,6 +282,7 @@
|
|||||||
<tomee-servlet-api.version>8.5.11</tomee-servlet-api.version>
|
<tomee-servlet-api.version>8.5.11</tomee-servlet-api.version>
|
||||||
<h2.version>1.4.194</h2.version>
|
<h2.version>1.4.194</h2.version>
|
||||||
<togglz.version>2.4.1.Final</togglz.version>
|
<togglz.version>2.4.1.Final</togglz.version>
|
||||||
|
<rome.version>1.9.0</rome.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -6,14 +6,12 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.LocaleResolver;
|
import org.springframework.web.servlet.LocaleResolver;
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
|
||||||
@ComponentScan(basePackages = "com.baeldung.internationalization.config")
|
@ComponentScan(basePackages = "com.baeldung.internationalization.config")
|
||||||
public class MvcConfig extends WebMvcConfigurerAdapter {
|
public class MvcConfig extends WebMvcConfigurerAdapter {
|
||||||
|
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.baeldung.rss;
|
||||||
|
|
||||||
|
import com.rometools.rome.feed.rss.Channel;
|
||||||
|
import com.rometools.rome.feed.rss.Description;
|
||||||
|
import com.rometools.rome.feed.rss.Item;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.servlet.view.feed.AbstractRssFeedView;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service("articleFeedView")
|
||||||
|
public class ArticleFeedView extends AbstractRssFeedView {
|
||||||
|
|
||||||
|
protected Channel newFeed() {
|
||||||
|
Channel channel = new Channel("rss_2.0");
|
||||||
|
channel.setLink("http://localhost:8080/rss");
|
||||||
|
channel.setTitle("Article Feed");
|
||||||
|
channel.setDescription("Article Feed Description");
|
||||||
|
channel.setPubDate(new Date());
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Item> buildFeedItems(Map<String, Object> map, HttpServletRequest httpStRequest, HttpServletResponse httpStResponse) throws Exception {
|
||||||
|
List list = new ArrayList<Item>();
|
||||||
|
|
||||||
|
Item item1 = new Item();
|
||||||
|
item1.setLink("http://www.baeldung.com/netty-exception-handling");
|
||||||
|
item1.setTitle("Exceptions in Netty");
|
||||||
|
Description description1 = new Description();
|
||||||
|
description1.setValue("In this quick article, we’ll be looking at exception handling in Netty.");
|
||||||
|
item1.setDescription(description1);
|
||||||
|
item1.setPubDate(new Date());
|
||||||
|
item1.setAuthor("Carlos");
|
||||||
|
|
||||||
|
Item item2 = new Item();
|
||||||
|
item2.setLink("http://www.baeldung.com/cockroachdb-java");
|
||||||
|
item2.setTitle("Guide to CockroachDB in Java");
|
||||||
|
Description description2 = new Description();
|
||||||
|
description2.setValue("This tutorial is an introductory guide to using CockroachDB with Java.");
|
||||||
|
item2.setDescription(description2);
|
||||||
|
item2.setPubDate(new Date());
|
||||||
|
item2.setAuthor("Baeldung");
|
||||||
|
|
||||||
|
list.add(item1);
|
||||||
|
list.add(item2);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.baeldung.rss;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping(value = "/rss", produces = "application/*")
|
||||||
|
public class ArticleRssController {
|
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.GET)
|
||||||
|
public String articleFeed() {
|
||||||
|
return "articleFeedView";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.baeldung.rss;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
|
||||||
|
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class CustomContainer implements EmbeddedServletContainerCustomizer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(ConfigurableEmbeddedServletContainer container) {
|
||||||
|
container.setPort(8080);
|
||||||
|
container.setContextPath("");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
spring-boot/src/main/java/com/baeldung/rss/RssApp.java
Normal file
20
spring-boot/src/main/java/com/baeldung/rss/RssApp.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package com.baeldung.rss;
|
||||||
|
|
||||||
|
import com.baeldung.autoconfiguration.MySQLAutoconfiguration;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
|
||||||
|
import javax.annotation.security.RolesAllowed;
|
||||||
|
|
||||||
|
@SpringBootApplication(exclude = MySQLAutoconfiguration.class)
|
||||||
|
@ComponentScan(basePackages = "com.baeldung.rss")
|
||||||
|
public class RssApp {
|
||||||
|
|
||||||
|
@RolesAllowed("*")
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.setProperty("security.basic.enabled", "false");
|
||||||
|
SpringApplication.run(RssApp.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,7 +4,6 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.ServletRequestBindingException;
|
|
||||||
import org.springframework.web.bind.ServletRequestUtils;
|
import org.springframework.web.bind.ServletRequestUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
@ -2,7 +2,6 @@ package com.baeldung.webjar;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
|
||||||
|
|
||||||
import com.baeldung.autoconfiguration.MySQLAutoconfiguration;
|
import com.baeldung.autoconfiguration.MySQLAutoconfiguration;
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import org.springframework.core.convert.TypeDescriptor;
|
|||||||
import org.springframework.core.convert.converter.GenericConverter;
|
import org.springframework.core.convert.converter.GenericConverter;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class GenericBigDecimalConverter implements GenericConverter {
|
public class GenericBigDecimalConverter implements GenericConverter {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package org.baeldung.boot.converter.controller;
|
package org.baeldung.boot.converter.controller;
|
||||||
|
|
||||||
import com.baeldung.toggle.Employee;
|
import com.baeldung.toggle.Employee;
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
|||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
$(document).ready(function() {
|
||||||
|
$("#locales").change(function () {
|
||||||
|
var selectedOption = $('#locales').val();
|
||||||
|
if (selectedOption != ''){
|
||||||
|
window.location.replace('international?lang=' + selectedOption);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
@ -4,16 +4,7 @@
|
|||||||
<meta charset="ISO-8859-1" />
|
<meta charset="ISO-8859-1" />
|
||||||
<title>Home</title>
|
<title>Home</title>
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||||
<script type="text/javascript">
|
<script src="internationalization.js"></script>
|
||||||
$(document).ready(function() {
|
|
||||||
$("#locales").change(function () {
|
|
||||||
var selectedOption = $('#locales').val();
|
|
||||||
if (selectedOption != ''){
|
|
||||||
window.location.replace('international?lang=' + selectedOption);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 th:text="#{greeting}"></h1>
|
<h1 th:text="#{greeting}"></h1>
|
||||||
|
@ -10,8 +10,6 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||||||
import org.springframework.mail.SimpleMailMessage;
|
import org.springframework.mail.SimpleMailMessage;
|
||||||
import org.springframework.mail.javamail.JavaMailSender;
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.test.context.web.WebAppConfiguration;
|
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
|
||||||
import org.subethamail.wiser.Wiser;
|
import org.subethamail.wiser.Wiser;
|
||||||
import org.subethamail.wiser.WiserMessage;
|
import org.subethamail.wiser.WiserMessage;
|
||||||
|
|
||||||
|
@ -12,13 +12,11 @@
|
|||||||
|
|
||||||
Client-side service discovery allows services to find and communicate with each other without hardcoding hostname and port. The only ‘fixed point’ in such an architecture consists of a service registry with which each service has to register.
|
Client-side service discovery allows services to find and communicate with each other without hardcoding hostname and port. The only ‘fixed point’ in such an architecture consists of a service registry with which each service has to register.
|
||||||
|
|
||||||
|
### Relevant Articles:
|
||||||
- [Intro to Spring Cloud Netflix - Hystrix](http://www.baeldung.com/spring-cloud-netflix-hystrix)
|
- [Intro to Spring Cloud Netflix - Hystrix](http://www.baeldung.com/spring-cloud-netflix-hystrix)
|
||||||
- [Dockerizing a Spring Boot Application](http://www.baeldung.com/dockerizing-spring-boot-application)
|
- [Dockerizing a Spring Boot Application](http://www.baeldung.com/dockerizing-spring-boot-application)
|
||||||
- [Introduction to Spring Cloud Rest Client with Netflix Ribbon](http://www.baeldung.com/spring-cloud-rest-client-with-netflix-ribbon)
|
- [Introduction to Spring Cloud Rest Client with Netflix Ribbon](http://www.baeldung.com/spring-cloud-rest-client-with-netflix-ribbon)
|
||||||
- [A Quick Guide to Spring Cloud Consul](http://www.baeldung.com/spring-cloud-consul)
|
- [A Quick Guide to Spring Cloud Consul](http://www.baeldung.com/spring-cloud-consul)
|
||||||
|
|
||||||
### Relevant Articles:
|
|
||||||
- [Introduction to Spring Cloud Rest Client with Netflix Ribbon](http://www.baeldung.com/spring-cloud-rest-client-with-netflix-ribbon)
|
|
||||||
- [An Introduction to Spring Cloud Zookeeper](http://www.baeldung.com/spring-cloud-zookeeper)
|
- [An Introduction to Spring Cloud Zookeeper](http://www.baeldung.com/spring-cloud-zookeeper)
|
||||||
|
- [Using a Spring Cloud App Starter](http://www.baeldung.com/using-a-spring-cloud-app-starter)
|
||||||
- [Spring Cloud Connectors and Heroku](http://www.baeldung.com/spring-cloud-heroku)
|
- [Spring Cloud Connectors and Heroku](http://www.baeldung.com/spring-cloud-heroku)
|
||||||
|
|
||||||
|
@ -19,3 +19,8 @@ to write the following in `application.properties`:
|
|||||||
cloud.aws.rds.spring-cloud-test-db
|
cloud.aws.rds.spring-cloud-test-db
|
||||||
cloud.aws.rds.spring-cloud-test-db.password=se3retpass
|
cloud.aws.rds.spring-cloud-test-db.password=se3retpass
|
||||||
```
|
```
|
||||||
|
Multiple application classes are available under this project. To launch InstanceProfileAwsApplication application, replace `start-class` under `pom.xml`:
|
||||||
|
|
||||||
|
```
|
||||||
|
<start-class>com.baeldung.spring.cloud.aws.InstanceProfileAwsApplication</start-class>
|
||||||
|
```
|
@ -19,6 +19,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
<start-class>com.baeldung.spring.cloud.aws.SpringCloudAwsApplication</start-class>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package com.baeldung.spring.cloud.aws;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import com.baeldung.spring.cloud.aws.s3.SpringCloudS3Service;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@ComponentScan("com.baeldung.spring.cloud.aws.s3")
|
||||||
|
public class InstanceProfileAwsApplication {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(InstanceProfileAwsApplication.class);
|
||||||
|
private static final String applicationConfig = "spring.config.name:application-instance-profile";
|
||||||
|
|
||||||
|
private static String bucketName;
|
||||||
|
private static String fileName = "sample-file.txt";
|
||||||
|
|
||||||
|
private static void setupResources() {
|
||||||
|
bucketName = "baeldung-test-" + UUID.randomUUID()
|
||||||
|
.toString();
|
||||||
|
try {
|
||||||
|
Files.write(Paths.get(fileName), "Hello World!".getBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
setupResources();
|
||||||
|
if (!new File(fileName).exists()) {
|
||||||
|
logger.warn("Not able to create {} file. Check your folder permissions.", fileName);
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpringApplication application = new SpringApplicationBuilder(InstanceProfileAwsApplication.class).properties(applicationConfig)
|
||||||
|
.build();
|
||||||
|
ConfigurableApplicationContext context = application.run(args);
|
||||||
|
SpringCloudS3Service service = context.getBean(SpringCloudS3Service.class);
|
||||||
|
|
||||||
|
// S3 bucket operations
|
||||||
|
service.createBucket(bucketName);
|
||||||
|
service.uploadObject(bucketName, fileName);
|
||||||
|
service.downloadObject(bucketName, fileName);
|
||||||
|
service.deleteBucket(bucketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package com.baeldung.spring.cloud.aws.s3;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.amazonaws.services.s3.AmazonS3;
|
||||||
|
import com.amazonaws.services.s3.model.ListObjectsV2Result;
|
||||||
|
import com.amazonaws.services.s3.model.S3ObjectSummary;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SpringCloudS3Service {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SpringCloudS3Service.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
AmazonS3 amazonS3;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
SpringCloudS3 springCloudS3;
|
||||||
|
|
||||||
|
public void createBucket(String bucketName) {
|
||||||
|
logger.debug("Creating S3 bucket: {}", bucketName);
|
||||||
|
amazonS3.createBucket(bucketName);
|
||||||
|
logger.info("{} bucket created successfully", bucketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void downloadObject(String bucketName, String objectName) {
|
||||||
|
String s3Url = "s3://" + bucketName + "/" + objectName;
|
||||||
|
try {
|
||||||
|
springCloudS3.downloadS3Object(s3Url);
|
||||||
|
logger.info("{} file download result: {}", objectName, new File(objectName).exists());
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void uploadObject(String bucketName, String objectName) {
|
||||||
|
String s3Url = "s3://" + bucketName + "/" + objectName;
|
||||||
|
File file = new File(objectName);
|
||||||
|
try {
|
||||||
|
springCloudS3.uploadFileToS3(file, s3Url);
|
||||||
|
logger.info("{} file uploaded to S3", objectName);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteBucket(String bucketName) {
|
||||||
|
logger.trace("Deleting S3 objects under {} bucket...", bucketName);
|
||||||
|
ListObjectsV2Result listObjectsV2Result = amazonS3.listObjectsV2(bucketName);
|
||||||
|
for (S3ObjectSummary objectSummary : listObjectsV2Result.getObjectSummaries()) {
|
||||||
|
logger.info("Deleting S3 object: {}", objectSummary.getKey());
|
||||||
|
amazonS3.deleteObject(bucketName, objectSummary.getKey());
|
||||||
|
}
|
||||||
|
logger.info("Deleting S3 bucket: {}", bucketName);
|
||||||
|
amazonS3.deleteBucket(bucketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
AWSTemplateFormatVersion: 2010-09-09
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
157e7d5f-5cb3-4a23-a50c-97e7f6c57173:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 450
|
||||||
|
'y': 90
|
||||||
|
z: 0
|
||||||
|
embeds: []
|
||||||
|
9bbaaa55-9cba-4555-a7c6-fb6ac248fd3a:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 260
|
||||||
|
'y': 90
|
||||||
|
z: 0
|
||||||
|
embeds: []
|
||||||
|
isassociatedwith:
|
||||||
|
- 157e7d5f-5cb3-4a23-a50c-97e7f6c57173
|
||||||
|
a7348729-a594-4dca-9b0a-e1c8d777dc3b:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 70
|
||||||
|
'y': 90
|
||||||
|
z: 0
|
||||||
|
embeds: []
|
||||||
|
Resources:
|
||||||
|
IAMRoleBaeldung:
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- ec2.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- 'arn:aws:iam::aws:policy/AmazonS3FullAccess'
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
id: 157e7d5f-5cb3-4a23-a50c-97e7f6c57173
|
||||||
|
InstanceProfileBaeldung:
|
||||||
|
Type: 'AWS::IAM::InstanceProfile'
|
||||||
|
Properties:
|
||||||
|
Roles:
|
||||||
|
- !Ref IAMRoleBaeldung
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
id: 9bbaaa55-9cba-4555-a7c6-fb6ac248fd3a
|
||||||
|
EC2Instance:
|
||||||
|
Type: 'AWS::EC2::Instance'
|
||||||
|
Properties:
|
||||||
|
ImageId: ami-2581aa40
|
||||||
|
InstanceType: t2.micro
|
||||||
|
IamInstanceProfile: !Ref InstanceProfileBaeldung
|
||||||
|
KeyName: Satish-Ohio
|
||||||
|
UserData: !Base64
|
||||||
|
'Fn::Join':
|
||||||
|
- ''
|
||||||
|
- - |
|
||||||
|
#!/bin/bash
|
||||||
|
- |
|
||||||
|
apt -y install openjdk-8-jre-headless
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
id: a7348729-a594-4dca-9b0a-e1c8d777dc3b
|
||||||
|
DependsOn:
|
||||||
|
- InstanceProfileBaeldung
|
||||||
|
|
@ -0,0 +1,14 @@
|
|||||||
|
# Don't try to create DataSouce when running tests which don't need a DataSource
|
||||||
|
spring.autoconfigure.exclude=\
|
||||||
|
org.springframework.cloud.aws.autoconfigure.jdbc.AmazonRdsDatabaseAutoConfiguration,\
|
||||||
|
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
|
||||||
|
cloud.aws.region.auto=true
|
||||||
|
|
||||||
|
# Load instance profile credentials
|
||||||
|
cloud.aws.credentials.instanceProfile=true
|
||||||
|
|
||||||
|
# Disable auto cloud formation
|
||||||
|
cloud.aws.stack.auto=false
|
||||||
|
|
||||||
|
# Disable web environment
|
||||||
|
spring.main.web-environment=false
|
46
spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh
Normal file
46
spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# For Ubuntu 14.04
|
||||||
|
# Inspired from: https://github.com/curran/setupHadoop/blob/master/setupHadoop.sh
|
||||||
|
# Use from the user directory
|
||||||
|
|
||||||
|
# Install Java
|
||||||
|
sudo apt-get update
|
||||||
|
sudo add-apt-repository -y ppa:webupd8team/java
|
||||||
|
sudo apt-get install -y oracle-java8-installer
|
||||||
|
|
||||||
|
# Install Hadoop
|
||||||
|
curl -O http://mirror.cogentco.com/pub/apache/hadoop/common/hadoop-2.8.2/hadoop-2.8.2.tar.gz
|
||||||
|
tar xfz hadoop-2.8.2.tar.gz
|
||||||
|
sudo mv hadoop-2.8.2 /usr/local/hadoop
|
||||||
|
rm hadoop-2.8.2.tar.gz
|
||||||
|
|
||||||
|
# Environmental Variables
|
||||||
|
echo export JAVA_HOME=/usr/lib/jvm/java-8-oracle >> ~/.bashrc
|
||||||
|
echo export HADOOP_PREFIX=/usr/local/hadoop >> ~/.bashrc
|
||||||
|
echo export PATH=\$PATH:/usr/local/hadoop/bin >> ~/.bashrc
|
||||||
|
echo export PATH=\$PATH:/usr/local/hadoop/sbin >> ~/.bashrc
|
||||||
|
source ~/.bashrc
|
||||||
|
|
||||||
|
# Copy configuration files
|
||||||
|
cp master/* /usr/local/hadoop/etc/hadoop/
|
||||||
|
|
||||||
|
# Format HDFS
|
||||||
|
hdfs namenode -format
|
||||||
|
|
||||||
|
# SSH keys for Hadoop to use.
|
||||||
|
ssh-keygen -t rsa -P 'password' -f ~/.ssh/id_rsa.pub
|
||||||
|
sudo mv ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
|
||||||
|
|
||||||
|
# SSH
|
||||||
|
ssh localhost
|
||||||
|
# authenticate with osboxes.org
|
||||||
|
|
||||||
|
# Start NameNode daemon and DataNode daemon
|
||||||
|
start-dfs.sh
|
||||||
|
# stop-dfs.sh
|
||||||
|
|
||||||
|
# Install Maven
|
||||||
|
sudo apt-get install maven
|
||||||
|
|
||||||
|
# Access Hadoop - http://localhost:50070
|
3
spring-cloud/spring-cloud-stream-starters/boot/.gitignore
vendored
Normal file
3
spring-cloud/spring-cloud-stream-starters/boot/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.idea
|
||||||
|
*/target/*
|
||||||
|
*.iml
|
55
spring-cloud/spring-cloud-stream-starters/boot/pom.xml
Normal file
55
spring-cloud/spring-cloud-stream-starters/boot/pom.xml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<project 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"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.baeldung.twitterhdfs</groupId>
|
||||||
|
<artifactId>twitterhdfs</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
|
||||||
|
<name>twitterhdfs</name>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>1.5.8.RELEASE</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring Stream Starter Apps -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-stream-source-twitterstream</artifactId>
|
||||||
|
<version>1.3.1.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-stream-sink-hdfs</artifactId>
|
||||||
|
<version>1.3.1.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JSTL/JSP -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>jstl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
<finalName>twitterhdfs</finalName>
|
||||||
|
</build>
|
||||||
|
</project>
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.baeldung.twitterhdfs.aggregate;
|
||||||
|
|
||||||
|
import com.baeldung.twitterhdfs.processor.ProcessorApp;
|
||||||
|
import com.baeldung.twitterhdfs.source.SourceApp;
|
||||||
|
import com.baeldung.twitterhdfs.sink.SinkApp;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.stream.aggregate.AggregateApplicationBuilder;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class AggregateApp {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new AggregateApplicationBuilder()
|
||||||
|
.from(SourceApp.class).args("--fixedDelay=5000")
|
||||||
|
.via(ProcessorApp.class)
|
||||||
|
.to(SinkApp.class).args("--debug=true")
|
||||||
|
.run(args);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.baeldung.twitterhdfs.processor;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||||
|
import org.springframework.cloud.stream.messaging.Processor;
|
||||||
|
import org.springframework.integration.annotation.Transformer;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableBinding(Processor.class)
|
||||||
|
public class ProcessorApp {
|
||||||
|
Logger log = LoggerFactory.getLogger(ProcessorApp.class);
|
||||||
|
|
||||||
|
@Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
|
||||||
|
public String processMessage(String payload) {
|
||||||
|
log.info("Payload received!");
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.baeldung.twitterhdfs.sink;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||||
|
import org.springframework.cloud.stream.app.hdfs.sink.HdfsSinkConfiguration;
|
||||||
|
import org.springframework.cloud.stream.messaging.Sink;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.integration.annotation.ServiceActivator;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableBinding(Sink.class)
|
||||||
|
@Import(HdfsSinkConfiguration.class)
|
||||||
|
public class SinkApp {
|
||||||
|
Logger log = LoggerFactory.getLogger(SinkApp.class);
|
||||||
|
|
||||||
|
@ServiceActivator(inputChannel= Sink.INPUT)
|
||||||
|
public void loggerSink(Object payload) {
|
||||||
|
log.info("Received: " + payload);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.baeldung.twitterhdfs.source;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||||
|
import org.springframework.cloud.stream.app.twitterstream.source.TwitterstreamSourceConfiguration;
|
||||||
|
import org.springframework.cloud.stream.messaging.Source;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.integration.annotation.InboundChannelAdapter;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableBinding(Source.class)
|
||||||
|
@Import(TwitterstreamSourceConfiguration.class)
|
||||||
|
public class SourceApp {
|
||||||
|
Logger log = LoggerFactory.getLogger(SourceApp.class);
|
||||||
|
|
||||||
|
@InboundChannelAdapter(value = Source.OUTPUT)
|
||||||
|
public String timerMessageSource() {
|
||||||
|
return new SimpleDateFormat().format(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
hdfs.fs-uri=hdfs://127.0.0.1:50010/
|
||||||
|
|
||||||
|
twitter.credentials.access-token=
|
||||||
|
twitter.credentials.access-token-secret=
|
||||||
|
twitter.credentials.consumer-key=
|
||||||
|
twitter.credentials.consumer-secret=
|
@ -0,0 +1 @@
|
|||||||
|
hdfs.fs-uri=hdfs://127.0.0.1:50010/
|
11
spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh
Normal file
11
spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Git spring-cloud-stream-app-starters
|
||||||
|
# https://github.com/spring-cloud-stream-app-starters/hdfs/blob/master/spring-cloud-starter-stream-sink-hdfs/README.adoc
|
||||||
|
git clone https://github.com/spring-cloud-stream-app-starters/hdfs.git
|
||||||
|
|
||||||
|
# Build it
|
||||||
|
./mvnw clean install -PgenerateApps
|
||||||
|
|
||||||
|
# Run it
|
||||||
|
cd target
|
||||||
|
# Optionally inject application.properties prior to build
|
||||||
|
java -jar hdfs-sink.jar --fsUri=hdfs://127.0.0.1:50010/
|
@ -0,0 +1,4 @@
|
|||||||
|
twitter.credentials.access-token=
|
||||||
|
twitter.credentials.access-token-secret=
|
||||||
|
twitter.credentials.consumer-key=
|
||||||
|
twitter.credentials.consumer-secret=
|
12
spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh
Normal file
12
spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Git spring-cloud-stream-app-starters
|
||||||
|
# https://github.com/spring-cloud-stream-app-starters/hdfs/blob/master/spring-cloud-starter-stream-sink-hdfs/README.adoc
|
||||||
|
git clone https://github.com/spring-cloud-stream-app-starters/twitter.git
|
||||||
|
|
||||||
|
# Build it
|
||||||
|
./mvnw clean install -PgenerateApps
|
||||||
|
|
||||||
|
# Run it
|
||||||
|
cd target
|
||||||
|
# Optionally inject application.properties prior to build
|
||||||
|
java -jar twitter_stream_source.jar --consumerKey=<CONSUMER_KEY> --consumerSecret=<CONSUMER_SECRET> \
|
||||||
|
--accessToken=<ACCESS_TOKEN> --accessTokenSecret=<ACCESS_TOKEN_SECRET>
|
@ -1,12 +1,13 @@
|
|||||||
package com.baeldung.spring.data.es.repository;
|
package com.baeldung.spring.data.es.repository;
|
||||||
|
|
||||||
import com.baeldung.spring.data.es.model.Article;
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.elasticsearch.annotations.Query;
|
import org.springframework.data.elasticsearch.annotations.Query;
|
||||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import com.baeldung.spring.data.es.model.Article;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface ArticleRepository extends ElasticsearchRepository<Article, String> {
|
public interface ArticleRepository extends ElasticsearchRepository<Article, String> {
|
||||||
|
|
||||||
@ -14,4 +15,10 @@ public interface ArticleRepository extends ElasticsearchRepository<Article, Stri
|
|||||||
|
|
||||||
@Query("{\"bool\": {\"must\": [{\"match\": {\"authors.name\": \"?0\"}}]}}")
|
@Query("{\"bool\": {\"must\": [{\"match\": {\"authors.name\": \"?0\"}}]}}")
|
||||||
Page<Article> findByAuthorsNameUsingCustomQuery(String name, Pageable pageable);
|
Page<Article> findByAuthorsNameUsingCustomQuery(String name, Pageable pageable);
|
||||||
|
|
||||||
|
@Query("{\"bool\": {\"must\": {\"match_all\": {}}, \"filter\": {\"term\": {\"tags\": \"?0\" }}}}")
|
||||||
|
Page<Article> findByFilteredTagQuery(String tag, Pageable pageable);
|
||||||
|
|
||||||
|
@Query("{\"bool\": {\"must\": {\"match\": {\"authors.name\": \"?0\"}}, \"filter\": {\"term\": {\"tags\": \"?1\" }}}}")
|
||||||
|
Page<Article> findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package com.baeldung.spring.data.es.service;
|
package com.baeldung.spring.data.es.service;
|
||||||
|
|
||||||
import com.baeldung.spring.data.es.model.Article;
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
|
import com.baeldung.spring.data.es.model.Article;
|
||||||
|
|
||||||
public interface ArticleService {
|
public interface ArticleService {
|
||||||
Article save(Article article);
|
Article save(Article article);
|
||||||
|
|
||||||
@ -15,6 +16,10 @@ public interface ArticleService {
|
|||||||
|
|
||||||
Page<Article> findByAuthorNameUsingCustomQuery(String name, Pageable pageable);
|
Page<Article> findByAuthorNameUsingCustomQuery(String name, Pageable pageable);
|
||||||
|
|
||||||
|
Page<Article> findByFilteredTagQuery(String tag, Pageable pageable);
|
||||||
|
|
||||||
|
Page<Article> findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable);
|
||||||
|
|
||||||
long count();
|
long count();
|
||||||
|
|
||||||
void delete(Article article);
|
void delete(Article article);
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package com.baeldung.spring.data.es.service;
|
package com.baeldung.spring.data.es.service;
|
||||||
|
|
||||||
import com.baeldung.spring.data.es.repository.ArticleRepository;
|
|
||||||
import com.baeldung.spring.data.es.model.Article;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.baeldung.spring.data.es.model.Article;
|
||||||
|
import com.baeldung.spring.data.es.repository.ArticleRepository;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ArticleServiceImpl implements ArticleService {
|
public class ArticleServiceImpl implements ArticleService {
|
||||||
|
|
||||||
@ -42,6 +43,16 @@ public class ArticleServiceImpl implements ArticleService {
|
|||||||
return articleRepository.findByAuthorsNameUsingCustomQuery(name, pageable);
|
return articleRepository.findByAuthorsNameUsingCustomQuery(name, pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<Article> findByFilteredTagQuery(String tag, Pageable pageable) {
|
||||||
|
return articleRepository.findByFilteredTagQuery(tag, pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<Article> findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable) {
|
||||||
|
return articleRepository.findByAuthorsNameAndFilteredTagQuery(name, tag, pageable);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long count() {
|
public long count() {
|
||||||
return articleRepository.count();
|
return articleRepository.count();
|
||||||
|
@ -46,14 +46,22 @@ public class ElasticSearchIntegrationTest {
|
|||||||
|
|
||||||
Article article = new Article("Spring Data Elasticsearch");
|
Article article = new Article("Spring Data Elasticsearch");
|
||||||
article.setAuthors(asList(johnSmith, johnDoe));
|
article.setAuthors(asList(johnSmith, johnDoe));
|
||||||
|
article.setTags("elasticsearch", "spring data");
|
||||||
articleService.save(article);
|
articleService.save(article);
|
||||||
|
|
||||||
article = new Article("Search engines");
|
article = new Article("Search engines");
|
||||||
article.setAuthors(asList(johnDoe));
|
article.setAuthors(asList(johnDoe));
|
||||||
|
article.setTags("search engines", "tutorial");
|
||||||
articleService.save(article);
|
articleService.save(article);
|
||||||
|
|
||||||
article = new Article("Second Article About Elasticsearch");
|
article = new Article("Second Article About Elasticsearch");
|
||||||
article.setAuthors(asList(johnSmith));
|
article.setAuthors(asList(johnSmith));
|
||||||
|
article.setTags("elasticsearch", "spring data");
|
||||||
|
articleService.save(article);
|
||||||
|
|
||||||
|
article = new Article("Elasticsearch Tutorial");
|
||||||
|
article.setAuthors(asList(johnDoe));
|
||||||
|
article.setTags("elasticsearch");
|
||||||
articleService.save(article);
|
articleService.save(article);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,12 +86,22 @@ public class ElasticSearchIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenCustomQuery_whenSearchByAuthorsName_thenArticleIsFound() {
|
public void givenCustomQuery_whenSearchByAuthorsName_thenArticleIsFound() {
|
||||||
|
final Page<Article> articleByAuthorName = articleService.findByAuthorNameUsingCustomQuery("Smith", new PageRequest(0, 10));
|
||||||
|
assertEquals(2L, articleByAuthorName.getTotalElements());
|
||||||
|
}
|
||||||
|
|
||||||
final Page<Article> articleByAuthorName = articleService
|
@Test
|
||||||
.findByAuthorNameUsingCustomQuery("John Smith", new PageRequest(0, 10));
|
public void givenTagFilterQuery_whenSearchByTag_thenArticleIsFound() {
|
||||||
|
final Page<Article> articleByAuthorName = articleService.findByFilteredTagQuery("elasticsearch", new PageRequest(0, 10));
|
||||||
assertEquals(3L, articleByAuthorName.getTotalElements());
|
assertEquals(3L, articleByAuthorName.getTotalElements());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenTagFilterQuery_whenSearchByAuthorsName_thenArticleIsFound() {
|
||||||
|
final Page<Article> articleByAuthorName = articleService.findByAuthorsNameAndFilteredTagQuery("Doe", "elasticsearch", new PageRequest(0, 10));
|
||||||
|
assertEquals(2L, articleByAuthorName.getTotalElements());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenPersistedArticles_whenUseRegexQuery_thenRightArticlesFound() {
|
public void givenPersistedArticles_whenUseRegexQuery_thenRightArticlesFound() {
|
||||||
|
|
||||||
|
@ -191,4 +191,16 @@ public class ElasticSearchQueryIntegrationTest {
|
|||||||
final List<Article> articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
|
final List<Article> articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
|
||||||
assertEquals(2, articles.size());
|
assertEquals(2, articles.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenBoolQuery_whenQueryByAuthorsName_thenFoundArticlesByThatAuthorAndFilteredTag() {
|
||||||
|
final QueryBuilder builder = boolQuery().must(nestedQuery("authors", boolQuery().must(termQuery("authors.name", "doe"))))
|
||||||
|
.filter(termQuery("tags", "elasticsearch"));
|
||||||
|
|
||||||
|
final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder)
|
||||||
|
.build();
|
||||||
|
final List<Article> articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
|
||||||
|
|
||||||
|
assertEquals(2, articles.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
1
spring-mvc-push/.gitignore
vendored
1
spring-mvc-push/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/.tern-project
|
|
@ -1,91 +0,0 @@
|
|||||||
<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/maven-v4_0_0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<groupId>com.baeldung</groupId>
|
|
||||||
<artifactId>spring-mvc-push</artifactId>
|
|
||||||
<packaging>war</packaging>
|
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
|
||||||
<name>spring-mvc-push</name>
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
|
||||||
<maven-surefire-plugin.version>2.20</maven-surefire-plugin.version>
|
|
||||||
<maven.compiler.version>3.7.0</maven.compiler.version>
|
|
||||||
<maven-war-plugin.version>3.2.0</maven-war-plugin.version>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
<junit.jupiter.version>5.0.2</junit.jupiter.version>
|
|
||||||
<spring.version>5.0.2.RELEASE</spring.version>
|
|
||||||
<servlet.version>4.0.0</servlet.version>
|
|
||||||
<jstl.version>1.2</jstl.version>
|
|
||||||
<jsp-api.version>2.3.2-b02</jsp-api.version>
|
|
||||||
<junit.jupiter.version>5.0.2</junit.jupiter.version>
|
|
||||||
<junit.platform.version>1.0.2</junit.platform.version>
|
|
||||||
</properties>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-webmvc</artifactId>
|
|
||||||
<version>${spring.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>javax.servlet</groupId>
|
|
||||||
<artifactId>javax.servlet-api</artifactId>
|
|
||||||
<version>${servlet.version}</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>javax.servlet</groupId>
|
|
||||||
<artifactId>jstl</artifactId>
|
|
||||||
<version>${jstl.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>javax.servlet.jsp</groupId>
|
|
||||||
<artifactId>javax.servlet.jsp-api</artifactId>
|
|
||||||
<version>${jsp-api.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<!--Testing -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-test</artifactId>
|
|
||||||
<version>${spring.version}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
|
||||||
<version>${junit.jupiter.version}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>${maven.compiler.version}</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-war-plugin</artifactId>
|
|
||||||
<version>${maven-war-plugin.version}</version>
|
|
||||||
<configuration>
|
|
||||||
<warName>spring-mvc-push</warName>
|
|
||||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
|
||||||
<outputDirectory>${deploy-path}</outputDirectory>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<version>${maven-surefire-plugin.version}</version>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.platform</groupId>
|
|
||||||
<artifactId>junit-platform-surefire-provider</artifactId>
|
|
||||||
<version>${junit.platform.version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
<finalName>spring-mvc-push</finalName>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
@ -1,48 +0,0 @@
|
|||||||
package com.baeldung.config;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.ServletRegistration;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.WebApplicationInitializer;
|
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
|
||||||
import org.springframework.web.servlet.DispatcherServlet;
|
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableWebMvc
|
|
||||||
@ComponentScan(basePackages = "com.baeldung.controller")
|
|
||||||
public class PushConfiguration implements WebApplicationInitializer, WebMvcConfigurer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStartup(ServletContext container) throws ServletException {
|
|
||||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
|
||||||
context.register(PushConfiguration.class);
|
|
||||||
container.addListener(new ContextLoaderListener(context));
|
|
||||||
ServletRegistration.Dynamic dispatcher = container.addServlet("DispatcherServlet", new DispatcherServlet(context));
|
|
||||||
dispatcher.setLoadOnStartup(1);
|
|
||||||
dispatcher.addMapping("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public InternalResourceViewResolver jspViewResolver() {
|
|
||||||
InternalResourceViewResolver bean = new InternalResourceViewResolver();
|
|
||||||
bean.setPrefix("/WEB-INF/views/");
|
|
||||||
bean.setSuffix(".jsp");
|
|
||||||
return bean;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
|
||||||
registry.addResourceHandler("/resources/**")
|
|
||||||
.addResourceLocations("/resources/");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
|
||||||
pageEncoding="UTF-8"%>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
<title>PushBuilder demo</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h4>
|
|
||||||
Go to <a href="demoWithPush">PushBuilder demo</a><br> Go to
|
|
||||||
<a href="demoWithoutPush">Simple demo</a>
|
|
||||||
</h4>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1 +0,0 @@
|
|||||||
console.log('Script')
|
|
@ -1,9 +0,0 @@
|
|||||||
.single-title {
|
|
||||||
font-size: 30px;
|
|
||||||
color: #535353;
|
|
||||||
font-weight: 200;
|
|
||||||
letter-spacing: -1.5px;
|
|
||||||
line-height: 64px;
|
|
||||||
max-width: 750px;
|
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
|
|
||||||
}
|
|
@ -2,3 +2,4 @@
|
|||||||
|
|
||||||
- [HandlerAdapters in Spring MVC](http://www.baeldung.com/spring-mvc-handler-adapters)
|
- [HandlerAdapters in Spring MVC](http://www.baeldung.com/spring-mvc-handler-adapters)
|
||||||
- [Template Engines for Spring](http://www.baeldung.com/spring-template-engines)
|
- [Template Engines for Spring](http://www.baeldung.com/spring-template-engines)
|
||||||
|
- [Spring 5 and Servlet 4 – The PushBuilder](http://www.baeldung.com/spring-5-push)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user